##// END OF EJS Templates
caches: configure defaults for all defined regions....
marcink -
r2367:1eecd373 default
parent child Browse files
Show More
@@ -1,436 +1,437 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 import logging
21 import logging
22 import traceback
22 import traceback
23 import collections
23 import collections
24
24
25 from paste.gzipper import make_gzip_middleware
25 from paste.gzipper import make_gzip_middleware
26 from pyramid.wsgi import wsgiapp
26 from pyramid.wsgi import wsgiapp
27 from pyramid.authorization import ACLAuthorizationPolicy
27 from pyramid.authorization import ACLAuthorizationPolicy
28 from pyramid.config import Configurator
28 from pyramid.config import Configurator
29 from pyramid.settings import asbool, aslist
29 from pyramid.settings import asbool, aslist
30 from pyramid.httpexceptions import (
30 from pyramid.httpexceptions import (
31 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
31 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
32 from pyramid.events import ApplicationCreated
32 from pyramid.events import ApplicationCreated
33 from pyramid.renderers import render_to_response
33 from pyramid.renderers import render_to_response
34
34
35 from rhodecode.model import meta
35 from rhodecode.model import meta
36 from rhodecode.config import patches
36 from rhodecode.config import patches
37 from rhodecode.config import utils as config_utils
37 from rhodecode.config import utils as config_utils
38 from rhodecode.config.environment import load_pyramid_environment
38 from rhodecode.config.environment import load_pyramid_environment
39
39
40 from rhodecode.lib.middleware.vcs import VCSMiddleware
40 from rhodecode.lib.middleware.vcs import VCSMiddleware
41 from rhodecode.lib.vcs import VCSCommunicationError
41 from rhodecode.lib.vcs import VCSCommunicationError
42 from rhodecode.lib.exceptions import VCSServerUnavailable
42 from rhodecode.lib.exceptions import VCSServerUnavailable
43 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
43 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
44 from rhodecode.lib.middleware.https_fixup import HttpsFixup
44 from rhodecode.lib.middleware.https_fixup import HttpsFixup
45 from rhodecode.lib.celerylib.loader import configure_celery
45 from rhodecode.lib.celerylib.loader import configure_celery
46 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
46 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
47 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
47 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
48 from rhodecode.subscribers import (
48 from rhodecode.subscribers import (
49 scan_repositories_if_enabled, write_js_routes_if_enabled,
49 scan_repositories_if_enabled, write_js_routes_if_enabled,
50 write_metadata_if_needed, inject_app_settings)
50 write_metadata_if_needed, inject_app_settings)
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def is_http_error(response):
56 def is_http_error(response):
57 # error which should have traceback
57 # error which should have traceback
58 return response.status_code > 499
58 return response.status_code > 499
59
59
60
60
61 def make_pyramid_app(global_config, **settings):
61 def make_pyramid_app(global_config, **settings):
62 """
62 """
63 Constructs the WSGI application based on Pyramid.
63 Constructs the WSGI application based on Pyramid.
64
64
65 Specials:
65 Specials:
66
66
67 * The application can also be integrated like a plugin via the call to
67 * The application can also be integrated like a plugin via the call to
68 `includeme`. This is accompanied with the other utility functions which
68 `includeme`. This is accompanied with the other utility functions which
69 are called. Changing this should be done with great care to not break
69 are called. Changing this should be done with great care to not break
70 cases when these fragments are assembled from another place.
70 cases when these fragments are assembled from another place.
71
71
72 """
72 """
73 sanitize_settings_and_apply_defaults(settings)
73 sanitize_settings_and_apply_defaults(settings)
74
74
75 config = Configurator(settings=settings)
75 config = Configurator(settings=settings)
76
76
77 # Apply compatibility patches
77 # Apply compatibility patches
78 patches.inspect_getargspec()
78 patches.inspect_getargspec()
79
79
80 load_pyramid_environment(global_config, settings)
80 load_pyramid_environment(global_config, settings)
81
81
82 # Static file view comes first
82 # Static file view comes first
83 includeme_first(config)
83 includeme_first(config)
84
84
85 includeme(config)
85 includeme(config)
86
86
87 pyramid_app = config.make_wsgi_app()
87 pyramid_app = config.make_wsgi_app()
88 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
88 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
89 pyramid_app.config = config
89 pyramid_app.config = config
90
90
91 config.configure_celery(global_config['__file__'])
91 config.configure_celery(global_config['__file__'])
92 # creating the app uses a connection - return it after we are done
92 # creating the app uses a connection - return it after we are done
93 meta.Session.remove()
93 meta.Session.remove()
94
94
95 log.info('Pyramid app %s created and configured.', pyramid_app)
95 log.info('Pyramid app %s created and configured.', pyramid_app)
96 return pyramid_app
96 return pyramid_app
97
97
98
98
99 def not_found_view(request):
99 def not_found_view(request):
100 """
100 """
101 This creates the view which should be registered as not-found-view to
101 This creates the view which should be registered as not-found-view to
102 pyramid.
102 pyramid.
103 """
103 """
104
104
105 if not getattr(request, 'vcs_call', None):
105 if not getattr(request, 'vcs_call', None):
106 # handle like regular case with our error_handler
106 # handle like regular case with our error_handler
107 return error_handler(HTTPNotFound(), request)
107 return error_handler(HTTPNotFound(), request)
108
108
109 # handle not found view as a vcs call
109 # handle not found view as a vcs call
110 settings = request.registry.settings
110 settings = request.registry.settings
111 ae_client = getattr(request, 'ae_client', None)
111 ae_client = getattr(request, 'ae_client', None)
112 vcs_app = VCSMiddleware(
112 vcs_app = VCSMiddleware(
113 HTTPNotFound(), request.registry, settings,
113 HTTPNotFound(), request.registry, settings,
114 appenlight_client=ae_client)
114 appenlight_client=ae_client)
115
115
116 return wsgiapp(vcs_app)(None, request)
116 return wsgiapp(vcs_app)(None, request)
117
117
118
118
119 def error_handler(exception, request):
119 def error_handler(exception, request):
120 import rhodecode
120 import rhodecode
121 from rhodecode.lib import helpers
121 from rhodecode.lib import helpers
122
122
123 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
123 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
124
124
125 base_response = HTTPInternalServerError()
125 base_response = HTTPInternalServerError()
126 # prefer original exception for the response since it may have headers set
126 # prefer original exception for the response since it may have headers set
127 if isinstance(exception, HTTPException):
127 if isinstance(exception, HTTPException):
128 base_response = exception
128 base_response = exception
129 elif isinstance(exception, VCSCommunicationError):
129 elif isinstance(exception, VCSCommunicationError):
130 base_response = VCSServerUnavailable()
130 base_response = VCSServerUnavailable()
131
131
132 if is_http_error(base_response):
132 if is_http_error(base_response):
133 log.exception(
133 log.exception(
134 'error occurred handling this request for path: %s', request.path)
134 'error occurred handling this request for path: %s', request.path)
135
135
136 error_explanation = base_response.explanation or str(base_response)
136 error_explanation = base_response.explanation or str(base_response)
137 if base_response.status_code == 404:
137 if base_response.status_code == 404:
138 error_explanation += " Or you don't have permission to access it."
138 error_explanation += " Or you don't have permission to access it."
139 c = AttributeDict()
139 c = AttributeDict()
140 c.error_message = base_response.status
140 c.error_message = base_response.status
141 c.error_explanation = error_explanation
141 c.error_explanation = error_explanation
142 c.visual = AttributeDict()
142 c.visual = AttributeDict()
143
143
144 c.visual.rhodecode_support_url = (
144 c.visual.rhodecode_support_url = (
145 request.registry.settings.get('rhodecode_support_url') or
145 request.registry.settings.get('rhodecode_support_url') or
146 request.route_url('rhodecode_support')
146 request.route_url('rhodecode_support')
147 )
147 )
148 c.redirect_time = 0
148 c.redirect_time = 0
149 c.rhodecode_name = rhodecode_title
149 c.rhodecode_name = rhodecode_title
150 if not c.rhodecode_name:
150 if not c.rhodecode_name:
151 c.rhodecode_name = 'Rhodecode'
151 c.rhodecode_name = 'Rhodecode'
152
152
153 c.causes = []
153 c.causes = []
154 if is_http_error(base_response):
154 if is_http_error(base_response):
155 c.causes.append('Server is overloaded.')
155 c.causes.append('Server is overloaded.')
156 c.causes.append('Server database connection is lost.')
156 c.causes.append('Server database connection is lost.')
157 c.causes.append('Server expected unhandled error.')
157 c.causes.append('Server expected unhandled error.')
158
158
159 if hasattr(base_response, 'causes'):
159 if hasattr(base_response, 'causes'):
160 c.causes = base_response.causes
160 c.causes = base_response.causes
161
161
162 c.messages = helpers.flash.pop_messages(request=request)
162 c.messages = helpers.flash.pop_messages(request=request)
163 c.traceback = traceback.format_exc()
163 c.traceback = traceback.format_exc()
164 response = render_to_response(
164 response = render_to_response(
165 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
165 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
166 response=base_response)
166 response=base_response)
167
167
168 return response
168 return response
169
169
170
170
171 def includeme_first(config):
171 def includeme_first(config):
172 # redirect automatic browser favicon.ico requests to correct place
172 # redirect automatic browser favicon.ico requests to correct place
173 def favicon_redirect(context, request):
173 def favicon_redirect(context, request):
174 return HTTPFound(
174 return HTTPFound(
175 request.static_path('rhodecode:public/images/favicon.ico'))
175 request.static_path('rhodecode:public/images/favicon.ico'))
176
176
177 config.add_view(favicon_redirect, route_name='favicon')
177 config.add_view(favicon_redirect, route_name='favicon')
178 config.add_route('favicon', '/favicon.ico')
178 config.add_route('favicon', '/favicon.ico')
179
179
180 def robots_redirect(context, request):
180 def robots_redirect(context, request):
181 return HTTPFound(
181 return HTTPFound(
182 request.static_path('rhodecode:public/robots.txt'))
182 request.static_path('rhodecode:public/robots.txt'))
183
183
184 config.add_view(robots_redirect, route_name='robots')
184 config.add_view(robots_redirect, route_name='robots')
185 config.add_route('robots', '/robots.txt')
185 config.add_route('robots', '/robots.txt')
186
186
187 config.add_static_view(
187 config.add_static_view(
188 '_static/deform', 'deform:static')
188 '_static/deform', 'deform:static')
189 config.add_static_view(
189 config.add_static_view(
190 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
190 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
191
191
192
192
193 def includeme(config):
193 def includeme(config):
194 settings = config.registry.settings
194 settings = config.registry.settings
195
195
196 # plugin information
196 # plugin information
197 config.registry.rhodecode_plugins = collections.OrderedDict()
197 config.registry.rhodecode_plugins = collections.OrderedDict()
198
198
199 config.add_directive(
199 config.add_directive(
200 'register_rhodecode_plugin', register_rhodecode_plugin)
200 'register_rhodecode_plugin', register_rhodecode_plugin)
201
201
202 config.add_directive('configure_celery', configure_celery)
202 config.add_directive('configure_celery', configure_celery)
203
203
204 if asbool(settings.get('appenlight', 'false')):
204 if asbool(settings.get('appenlight', 'false')):
205 config.include('appenlight_client.ext.pyramid_tween')
205 config.include('appenlight_client.ext.pyramid_tween')
206
206
207 # Includes which are required. The application would fail without them.
207 # Includes which are required. The application would fail without them.
208 config.include('pyramid_mako')
208 config.include('pyramid_mako')
209 config.include('pyramid_beaker')
209 config.include('pyramid_beaker')
210 config.include('rhodecode.lib.caches')
210
211
211 config.include('rhodecode.authentication')
212 config.include('rhodecode.authentication')
212 config.include('rhodecode.integrations')
213 config.include('rhodecode.integrations')
213
214
214 # apps
215 # apps
215 config.include('rhodecode.apps._base')
216 config.include('rhodecode.apps._base')
216 config.include('rhodecode.apps.ops')
217 config.include('rhodecode.apps.ops')
217
218
218 config.include('rhodecode.apps.admin')
219 config.include('rhodecode.apps.admin')
219 config.include('rhodecode.apps.channelstream')
220 config.include('rhodecode.apps.channelstream')
220 config.include('rhodecode.apps.login')
221 config.include('rhodecode.apps.login')
221 config.include('rhodecode.apps.home')
222 config.include('rhodecode.apps.home')
222 config.include('rhodecode.apps.journal')
223 config.include('rhodecode.apps.journal')
223 config.include('rhodecode.apps.repository')
224 config.include('rhodecode.apps.repository')
224 config.include('rhodecode.apps.repo_group')
225 config.include('rhodecode.apps.repo_group')
225 config.include('rhodecode.apps.user_group')
226 config.include('rhodecode.apps.user_group')
226 config.include('rhodecode.apps.search')
227 config.include('rhodecode.apps.search')
227 config.include('rhodecode.apps.user_profile')
228 config.include('rhodecode.apps.user_profile')
228 config.include('rhodecode.apps.my_account')
229 config.include('rhodecode.apps.my_account')
229 config.include('rhodecode.apps.svn_support')
230 config.include('rhodecode.apps.svn_support')
230 config.include('rhodecode.apps.ssh_support')
231 config.include('rhodecode.apps.ssh_support')
231 config.include('rhodecode.apps.gist')
232 config.include('rhodecode.apps.gist')
232
233
233 config.include('rhodecode.apps.debug_style')
234 config.include('rhodecode.apps.debug_style')
234 config.include('rhodecode.tweens')
235 config.include('rhodecode.tweens')
235 config.include('rhodecode.api')
236 config.include('rhodecode.api')
236
237
237 config.add_route(
238 config.add_route(
238 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
239 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
239
240
240 config.add_translation_dirs('rhodecode:i18n/')
241 config.add_translation_dirs('rhodecode:i18n/')
241 settings['default_locale_name'] = settings.get('lang', 'en')
242 settings['default_locale_name'] = settings.get('lang', 'en')
242
243
243 # Add subscribers.
244 # Add subscribers.
244 config.add_subscriber(inject_app_settings, ApplicationCreated)
245 config.add_subscriber(inject_app_settings, ApplicationCreated)
245 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
246 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
246 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
247 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
247 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
248 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
248
249
249 # events
250 # events
250 # TODO(marcink): this should be done when pyramid migration is finished
251 # TODO(marcink): this should be done when pyramid migration is finished
251 # config.add_subscriber(
252 # config.add_subscriber(
252 # 'rhodecode.integrations.integrations_event_handler',
253 # 'rhodecode.integrations.integrations_event_handler',
253 # 'rhodecode.events.RhodecodeEvent')
254 # 'rhodecode.events.RhodecodeEvent')
254
255
255 # request custom methods
256 # request custom methods
256 config.add_request_method(
257 config.add_request_method(
257 'rhodecode.lib.partial_renderer.get_partial_renderer',
258 'rhodecode.lib.partial_renderer.get_partial_renderer',
258 'get_partial_renderer')
259 'get_partial_renderer')
259
260
260 # Set the authorization policy.
261 # Set the authorization policy.
261 authz_policy = ACLAuthorizationPolicy()
262 authz_policy = ACLAuthorizationPolicy()
262 config.set_authorization_policy(authz_policy)
263 config.set_authorization_policy(authz_policy)
263
264
264 # Set the default renderer for HTML templates to mako.
265 # Set the default renderer for HTML templates to mako.
265 config.add_mako_renderer('.html')
266 config.add_mako_renderer('.html')
266
267
267 config.add_renderer(
268 config.add_renderer(
268 name='json_ext',
269 name='json_ext',
269 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
270 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
270
271
271 # include RhodeCode plugins
272 # include RhodeCode plugins
272 includes = aslist(settings.get('rhodecode.includes', []))
273 includes = aslist(settings.get('rhodecode.includes', []))
273 for inc in includes:
274 for inc in includes:
274 config.include(inc)
275 config.include(inc)
275
276
276 # custom not found view, if our pyramid app doesn't know how to handle
277 # custom not found view, if our pyramid app doesn't know how to handle
277 # the request pass it to potential VCS handling ap
278 # the request pass it to potential VCS handling ap
278 config.add_notfound_view(not_found_view)
279 config.add_notfound_view(not_found_view)
279 if not settings.get('debugtoolbar.enabled', False):
280 if not settings.get('debugtoolbar.enabled', False):
280 # disabled debugtoolbar handle all exceptions via the error_handlers
281 # disabled debugtoolbar handle all exceptions via the error_handlers
281 config.add_view(error_handler, context=Exception)
282 config.add_view(error_handler, context=Exception)
282
283
283 # all errors including 403/404/50X
284 # all errors including 403/404/50X
284 config.add_view(error_handler, context=HTTPError)
285 config.add_view(error_handler, context=HTTPError)
285
286
286
287
287 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
288 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
288 """
289 """
289 Apply outer WSGI middlewares around the application.
290 Apply outer WSGI middlewares around the application.
290 """
291 """
291 settings = config.registry.settings
292 settings = config.registry.settings
292
293
293 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
294 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
294 pyramid_app = HttpsFixup(pyramid_app, settings)
295 pyramid_app = HttpsFixup(pyramid_app, settings)
295
296
296 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
297 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
297 pyramid_app, settings)
298 pyramid_app, settings)
298 config.registry.ae_client = _ae_client
299 config.registry.ae_client = _ae_client
299
300
300 if settings['gzip_responses']:
301 if settings['gzip_responses']:
301 pyramid_app = make_gzip_middleware(
302 pyramid_app = make_gzip_middleware(
302 pyramid_app, settings, compress_level=1)
303 pyramid_app, settings, compress_level=1)
303
304
304 # this should be the outer most middleware in the wsgi stack since
305 # this should be the outer most middleware in the wsgi stack since
305 # middleware like Routes make database calls
306 # middleware like Routes make database calls
306 def pyramid_app_with_cleanup(environ, start_response):
307 def pyramid_app_with_cleanup(environ, start_response):
307 try:
308 try:
308 return pyramid_app(environ, start_response)
309 return pyramid_app(environ, start_response)
309 finally:
310 finally:
310 # Dispose current database session and rollback uncommitted
311 # Dispose current database session and rollback uncommitted
311 # transactions.
312 # transactions.
312 meta.Session.remove()
313 meta.Session.remove()
313
314
314 # In a single threaded mode server, on non sqlite db we should have
315 # In a single threaded mode server, on non sqlite db we should have
315 # '0 Current Checked out connections' at the end of a request,
316 # '0 Current Checked out connections' at the end of a request,
316 # if not, then something, somewhere is leaving a connection open
317 # if not, then something, somewhere is leaving a connection open
317 pool = meta.Base.metadata.bind.engine.pool
318 pool = meta.Base.metadata.bind.engine.pool
318 log.debug('sa pool status: %s', pool.status())
319 log.debug('sa pool status: %s', pool.status())
319
320
320 return pyramid_app_with_cleanup
321 return pyramid_app_with_cleanup
321
322
322
323
323 def sanitize_settings_and_apply_defaults(settings):
324 def sanitize_settings_and_apply_defaults(settings):
324 """
325 """
325 Applies settings defaults and does all type conversion.
326 Applies settings defaults and does all type conversion.
326
327
327 We would move all settings parsing and preparation into this place, so that
328 We would move all settings parsing and preparation into this place, so that
328 we have only one place left which deals with this part. The remaining parts
329 we have only one place left which deals with this part. The remaining parts
329 of the application would start to rely fully on well prepared settings.
330 of the application would start to rely fully on well prepared settings.
330
331
331 This piece would later be split up per topic to avoid a big fat monster
332 This piece would later be split up per topic to avoid a big fat monster
332 function.
333 function.
333 """
334 """
334
335
335 settings.setdefault('rhodecode.edition', 'Community Edition')
336 settings.setdefault('rhodecode.edition', 'Community Edition')
336
337
337 if 'mako.default_filters' not in settings:
338 if 'mako.default_filters' not in settings:
338 # set custom default filters if we don't have it defined
339 # set custom default filters if we don't have it defined
339 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
340 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
340 settings['mako.default_filters'] = 'h_filter'
341 settings['mako.default_filters'] = 'h_filter'
341
342
342 if 'mako.directories' not in settings:
343 if 'mako.directories' not in settings:
343 mako_directories = settings.setdefault('mako.directories', [
344 mako_directories = settings.setdefault('mako.directories', [
344 # Base templates of the original application
345 # Base templates of the original application
345 'rhodecode:templates',
346 'rhodecode:templates',
346 ])
347 ])
347 log.debug(
348 log.debug(
348 "Using the following Mako template directories: %s",
349 "Using the following Mako template directories: %s",
349 mako_directories)
350 mako_directories)
350
351
351 # Default includes, possible to change as a user
352 # Default includes, possible to change as a user
352 pyramid_includes = settings.setdefault('pyramid.includes', [
353 pyramid_includes = settings.setdefault('pyramid.includes', [
353 'rhodecode.lib.middleware.request_wrapper',
354 'rhodecode.lib.middleware.request_wrapper',
354 ])
355 ])
355 log.debug(
356 log.debug(
356 "Using the following pyramid.includes: %s",
357 "Using the following pyramid.includes: %s",
357 pyramid_includes)
358 pyramid_includes)
358
359
359 # TODO: johbo: Re-think this, usually the call to config.include
360 # TODO: johbo: Re-think this, usually the call to config.include
360 # should allow to pass in a prefix.
361 # should allow to pass in a prefix.
361 settings.setdefault('rhodecode.api.url', '/_admin/api')
362 settings.setdefault('rhodecode.api.url', '/_admin/api')
362
363
363 # Sanitize generic settings.
364 # Sanitize generic settings.
364 _list_setting(settings, 'default_encoding', 'UTF-8')
365 _list_setting(settings, 'default_encoding', 'UTF-8')
365 _bool_setting(settings, 'is_test', 'false')
366 _bool_setting(settings, 'is_test', 'false')
366 _bool_setting(settings, 'gzip_responses', 'false')
367 _bool_setting(settings, 'gzip_responses', 'false')
367
368
368 # Call split out functions that sanitize settings for each topic.
369 # Call split out functions that sanitize settings for each topic.
369 _sanitize_appenlight_settings(settings)
370 _sanitize_appenlight_settings(settings)
370 _sanitize_vcs_settings(settings)
371 _sanitize_vcs_settings(settings)
371
372
372 # configure instance id
373 # configure instance id
373 config_utils.set_instance_id(settings)
374 config_utils.set_instance_id(settings)
374
375
375 return settings
376 return settings
376
377
377
378
378 def _sanitize_appenlight_settings(settings):
379 def _sanitize_appenlight_settings(settings):
379 _bool_setting(settings, 'appenlight', 'false')
380 _bool_setting(settings, 'appenlight', 'false')
380
381
381
382
382 def _sanitize_vcs_settings(settings):
383 def _sanitize_vcs_settings(settings):
383 """
384 """
384 Applies settings defaults and does type conversion for all VCS related
385 Applies settings defaults and does type conversion for all VCS related
385 settings.
386 settings.
386 """
387 """
387 _string_setting(settings, 'vcs.svn.compatible_version', '')
388 _string_setting(settings, 'vcs.svn.compatible_version', '')
388 _string_setting(settings, 'git_rev_filter', '--all')
389 _string_setting(settings, 'git_rev_filter', '--all')
389 _string_setting(settings, 'vcs.hooks.protocol', 'http')
390 _string_setting(settings, 'vcs.hooks.protocol', 'http')
390 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
391 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
391 _string_setting(settings, 'vcs.server', '')
392 _string_setting(settings, 'vcs.server', '')
392 _string_setting(settings, 'vcs.server.log_level', 'debug')
393 _string_setting(settings, 'vcs.server.log_level', 'debug')
393 _string_setting(settings, 'vcs.server.protocol', 'http')
394 _string_setting(settings, 'vcs.server.protocol', 'http')
394 _bool_setting(settings, 'startup.import_repos', 'false')
395 _bool_setting(settings, 'startup.import_repos', 'false')
395 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
396 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
396 _bool_setting(settings, 'vcs.server.enable', 'true')
397 _bool_setting(settings, 'vcs.server.enable', 'true')
397 _bool_setting(settings, 'vcs.start_server', 'false')
398 _bool_setting(settings, 'vcs.start_server', 'false')
398 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
399 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
399 _int_setting(settings, 'vcs.connection_timeout', 3600)
400 _int_setting(settings, 'vcs.connection_timeout', 3600)
400
401
401 # Support legacy values of vcs.scm_app_implementation. Legacy
402 # Support legacy values of vcs.scm_app_implementation. Legacy
402 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
403 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
403 # which is now mapped to 'http'.
404 # which is now mapped to 'http'.
404 scm_app_impl = settings['vcs.scm_app_implementation']
405 scm_app_impl = settings['vcs.scm_app_implementation']
405 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
406 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
406 settings['vcs.scm_app_implementation'] = 'http'
407 settings['vcs.scm_app_implementation'] = 'http'
407
408
408
409
409 def _int_setting(settings, name, default):
410 def _int_setting(settings, name, default):
410 settings[name] = int(settings.get(name, default))
411 settings[name] = int(settings.get(name, default))
411
412
412
413
413 def _bool_setting(settings, name, default):
414 def _bool_setting(settings, name, default):
414 input_val = settings.get(name, default)
415 input_val = settings.get(name, default)
415 if isinstance(input_val, unicode):
416 if isinstance(input_val, unicode):
416 input_val = input_val.encode('utf8')
417 input_val = input_val.encode('utf8')
417 settings[name] = asbool(input_val)
418 settings[name] = asbool(input_val)
418
419
419
420
420 def _list_setting(settings, name, default):
421 def _list_setting(settings, name, default):
421 raw_value = settings.get(name, default)
422 raw_value = settings.get(name, default)
422
423
423 old_separator = ','
424 old_separator = ','
424 if old_separator in raw_value:
425 if old_separator in raw_value:
425 # If we get a comma separated list, pass it to our own function.
426 # If we get a comma separated list, pass it to our own function.
426 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
427 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
427 else:
428 else:
428 # Otherwise we assume it uses pyramids space/newline separation.
429 # Otherwise we assume it uses pyramids space/newline separation.
429 settings[name] = aslist(raw_value)
430 settings[name] = aslist(raw_value)
430
431
431
432
432 def _string_setting(settings, name, default, lower=True):
433 def _string_setting(settings, name, default, lower=True):
433 value = settings.get(name, default)
434 value = settings.get(name, default)
434 if lower:
435 if lower:
435 value = value.lower()
436 value = value.lower()
436 settings[name] = value
437 settings[name] = value
@@ -1,540 +1,541 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import markupsafe
30 import markupsafe
31 import ipaddress
31 import ipaddress
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.authentication.base import VCS_TYPE
38 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.lib import auth, utils2
39 from rhodecode.lib import auth, utils2
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
41 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
41 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 from rhodecode.lib.exceptions import UserCreationError
42 from rhodecode.lib.exceptions import UserCreationError
43 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
43 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils2 import (
44 from rhodecode.lib.utils2 import (
45 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist, safe_str)
45 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist, safe_str)
46 from rhodecode.model.db import Repository, User, ChangesetComment
46 from rhodecode.model.db import Repository, User, ChangesetComment
47 from rhodecode.model.notification import NotificationModel
47 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
48 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 def _filter_proxy(ip):
53 def _filter_proxy(ip):
54 """
54 """
55 Passed in IP addresses in HEADERS can be in a special format of multiple
55 Passed in IP addresses in HEADERS can be in a special format of multiple
56 ips. Those comma separated IPs are passed from various proxies in the
56 ips. Those comma separated IPs are passed from various proxies in the
57 chain of request processing. The left-most being the original client.
57 chain of request processing. The left-most being the original client.
58 We only care about the first IP which came from the org. client.
58 We only care about the first IP which came from the org. client.
59
59
60 :param ip: ip string from headers
60 :param ip: ip string from headers
61 """
61 """
62 if ',' in ip:
62 if ',' in ip:
63 _ips = ip.split(',')
63 _ips = ip.split(',')
64 _first_ip = _ips[0].strip()
64 _first_ip = _ips[0].strip()
65 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
65 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 return _first_ip
66 return _first_ip
67 return ip
67 return ip
68
68
69
69
70 def _filter_port(ip):
70 def _filter_port(ip):
71 """
71 """
72 Removes a port from ip, there are 4 main cases to handle here.
72 Removes a port from ip, there are 4 main cases to handle here.
73 - ipv4 eg. 127.0.0.1
73 - ipv4 eg. 127.0.0.1
74 - ipv6 eg. ::1
74 - ipv6 eg. ::1
75 - ipv4+port eg. 127.0.0.1:8080
75 - ipv4+port eg. 127.0.0.1:8080
76 - ipv6+port eg. [::1]:8080
76 - ipv6+port eg. [::1]:8080
77
77
78 :param ip:
78 :param ip:
79 """
79 """
80 def is_ipv6(ip_addr):
80 def is_ipv6(ip_addr):
81 if hasattr(socket, 'inet_pton'):
81 if hasattr(socket, 'inet_pton'):
82 try:
82 try:
83 socket.inet_pton(socket.AF_INET6, ip_addr)
83 socket.inet_pton(socket.AF_INET6, ip_addr)
84 except socket.error:
84 except socket.error:
85 return False
85 return False
86 else:
86 else:
87 # fallback to ipaddress
87 # fallback to ipaddress
88 try:
88 try:
89 ipaddress.IPv6Address(safe_unicode(ip_addr))
89 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 except Exception:
90 except Exception:
91 return False
91 return False
92 return True
92 return True
93
93
94 if ':' not in ip: # must be ipv4 pure ip
94 if ':' not in ip: # must be ipv4 pure ip
95 return ip
95 return ip
96
96
97 if '[' in ip and ']' in ip: # ipv6 with port
97 if '[' in ip and ']' in ip: # ipv6 with port
98 return ip.split(']')[0][1:].lower()
98 return ip.split(']')[0][1:].lower()
99
99
100 # must be ipv6 or ipv4 with port
100 # must be ipv6 or ipv4 with port
101 if is_ipv6(ip):
101 if is_ipv6(ip):
102 return ip
102 return ip
103 else:
103 else:
104 ip, _port = ip.split(':')[:2] # means ipv4+port
104 ip, _port = ip.split(':')[:2] # means ipv4+port
105 return ip
105 return ip
106
106
107
107
108 def get_ip_addr(environ):
108 def get_ip_addr(environ):
109 proxy_key = 'HTTP_X_REAL_IP'
109 proxy_key = 'HTTP_X_REAL_IP'
110 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
110 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 def_key = 'REMOTE_ADDR'
111 def_key = 'REMOTE_ADDR'
112 _filters = lambda x: _filter_port(_filter_proxy(x))
112 _filters = lambda x: _filter_port(_filter_proxy(x))
113
113
114 ip = environ.get(proxy_key)
114 ip = environ.get(proxy_key)
115 if ip:
115 if ip:
116 return _filters(ip)
116 return _filters(ip)
117
117
118 ip = environ.get(proxy_key2)
118 ip = environ.get(proxy_key2)
119 if ip:
119 if ip:
120 return _filters(ip)
120 return _filters(ip)
121
121
122 ip = environ.get(def_key, '0.0.0.0')
122 ip = environ.get(def_key, '0.0.0.0')
123 return _filters(ip)
123 return _filters(ip)
124
124
125
125
126 def get_server_ip_addr(environ, log_errors=True):
126 def get_server_ip_addr(environ, log_errors=True):
127 hostname = environ.get('SERVER_NAME')
127 hostname = environ.get('SERVER_NAME')
128 try:
128 try:
129 return socket.gethostbyname(hostname)
129 return socket.gethostbyname(hostname)
130 except Exception as e:
130 except Exception as e:
131 if log_errors:
131 if log_errors:
132 # in some cases this lookup is not possible, and we don't want to
132 # in some cases this lookup is not possible, and we don't want to
133 # make it an exception in logs
133 # make it an exception in logs
134 log.exception('Could not retrieve server ip address: %s', e)
134 log.exception('Could not retrieve server ip address: %s', e)
135 return hostname
135 return hostname
136
136
137
137
138 def get_server_port(environ):
138 def get_server_port(environ):
139 return environ.get('SERVER_PORT')
139 return environ.get('SERVER_PORT')
140
140
141
141
142 def get_access_path(environ):
142 def get_access_path(environ):
143 path = environ.get('PATH_INFO')
143 path = environ.get('PATH_INFO')
144 org_req = environ.get('pylons.original_request')
144 org_req = environ.get('pylons.original_request')
145 if org_req:
145 if org_req:
146 path = org_req.environ.get('PATH_INFO')
146 path = org_req.environ.get('PATH_INFO')
147 return path
147 return path
148
148
149
149
150 def get_user_agent(environ):
150 def get_user_agent(environ):
151 return environ.get('HTTP_USER_AGENT')
151 return environ.get('HTTP_USER_AGENT')
152
152
153
153
154 def vcs_operation_context(
154 def vcs_operation_context(
155 environ, repo_name, username, action, scm, check_locking=True,
155 environ, repo_name, username, action, scm, check_locking=True,
156 is_shadow_repo=False):
156 is_shadow_repo=False):
157 """
157 """
158 Generate the context for a vcs operation, e.g. push or pull.
158 Generate the context for a vcs operation, e.g. push or pull.
159
159
160 This context is passed over the layers so that hooks triggered by the
160 This context is passed over the layers so that hooks triggered by the
161 vcs operation know details like the user, the user's IP address etc.
161 vcs operation know details like the user, the user's IP address etc.
162
162
163 :param check_locking: Allows to switch of the computation of the locking
163 :param check_locking: Allows to switch of the computation of the locking
164 data. This serves mainly the need of the simplevcs middleware to be
164 data. This serves mainly the need of the simplevcs middleware to be
165 able to disable this for certain operations.
165 able to disable this for certain operations.
166
166
167 """
167 """
168 # Tri-state value: False: unlock, None: nothing, True: lock
168 # Tri-state value: False: unlock, None: nothing, True: lock
169 make_lock = None
169 make_lock = None
170 locked_by = [None, None, None]
170 locked_by = [None, None, None]
171 is_anonymous = username == User.DEFAULT_USER
171 is_anonymous = username == User.DEFAULT_USER
172 if not is_anonymous and check_locking:
172 if not is_anonymous and check_locking:
173 log.debug('Checking locking on repository "%s"', repo_name)
173 log.debug('Checking locking on repository "%s"', repo_name)
174 user = User.get_by_username(username)
174 user = User.get_by_username(username)
175 repo = Repository.get_by_repo_name(repo_name)
175 repo = Repository.get_by_repo_name(repo_name)
176 make_lock, __, locked_by = repo.get_locking_state(
176 make_lock, __, locked_by = repo.get_locking_state(
177 action, user.user_id)
177 action, user.user_id)
178
178
179 settings_model = VcsSettingsModel(repo=repo_name)
179 settings_model = VcsSettingsModel(repo=repo_name)
180 ui_settings = settings_model.get_ui_settings()
180 ui_settings = settings_model.get_ui_settings()
181
181
182 extras = {
182 extras = {
183 'ip': get_ip_addr(environ),
183 'ip': get_ip_addr(environ),
184 'username': username,
184 'username': username,
185 'action': action,
185 'action': action,
186 'repository': repo_name,
186 'repository': repo_name,
187 'scm': scm,
187 'scm': scm,
188 'config': rhodecode.CONFIG['__file__'],
188 'config': rhodecode.CONFIG['__file__'],
189 'make_lock': make_lock,
189 'make_lock': make_lock,
190 'locked_by': locked_by,
190 'locked_by': locked_by,
191 'server_url': utils2.get_server_url(environ),
191 'server_url': utils2.get_server_url(environ),
192 'user_agent': get_user_agent(environ),
192 'user_agent': get_user_agent(environ),
193 'hooks': get_enabled_hook_classes(ui_settings),
193 'hooks': get_enabled_hook_classes(ui_settings),
194 'is_shadow_repo': is_shadow_repo,
194 'is_shadow_repo': is_shadow_repo,
195 }
195 }
196 return extras
196 return extras
197
197
198
198
199 class BasicAuth(AuthBasicAuthenticator):
199 class BasicAuth(AuthBasicAuthenticator):
200
200
201 def __init__(self, realm, authfunc, registry, auth_http_code=None,
201 def __init__(self, realm, authfunc, registry, auth_http_code=None,
202 initial_call_detection=False, acl_repo_name=None):
202 initial_call_detection=False, acl_repo_name=None):
203 self.realm = realm
203 self.realm = realm
204 self.initial_call = initial_call_detection
204 self.initial_call = initial_call_detection
205 self.authfunc = authfunc
205 self.authfunc = authfunc
206 self.registry = registry
206 self.registry = registry
207 self.acl_repo_name = acl_repo_name
207 self.acl_repo_name = acl_repo_name
208 self._rc_auth_http_code = auth_http_code
208 self._rc_auth_http_code = auth_http_code
209
209
210 def _get_response_from_code(self, http_code):
210 def _get_response_from_code(self, http_code):
211 try:
211 try:
212 return get_exception(safe_int(http_code))
212 return get_exception(safe_int(http_code))
213 except Exception:
213 except Exception:
214 log.exception('Failed to fetch response for code %s' % http_code)
214 log.exception('Failed to fetch response for code %s' % http_code)
215 return HTTPForbidden
215 return HTTPForbidden
216
216
217 def get_rc_realm(self):
217 def get_rc_realm(self):
218 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
218 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
219
219
220 def build_authentication(self):
220 def build_authentication(self):
221 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
221 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
222 if self._rc_auth_http_code and not self.initial_call:
222 if self._rc_auth_http_code and not self.initial_call:
223 # return alternative HTTP code if alternative http return code
223 # return alternative HTTP code if alternative http return code
224 # is specified in RhodeCode config, but ONLY if it's not the
224 # is specified in RhodeCode config, but ONLY if it's not the
225 # FIRST call
225 # FIRST call
226 custom_response_klass = self._get_response_from_code(
226 custom_response_klass = self._get_response_from_code(
227 self._rc_auth_http_code)
227 self._rc_auth_http_code)
228 return custom_response_klass(headers=head)
228 return custom_response_klass(headers=head)
229 return HTTPUnauthorized(headers=head)
229 return HTTPUnauthorized(headers=head)
230
230
231 def authenticate(self, environ):
231 def authenticate(self, environ):
232 authorization = AUTHORIZATION(environ)
232 authorization = AUTHORIZATION(environ)
233 if not authorization:
233 if not authorization:
234 return self.build_authentication()
234 return self.build_authentication()
235 (authmeth, auth) = authorization.split(' ', 1)
235 (authmeth, auth) = authorization.split(' ', 1)
236 if 'basic' != authmeth.lower():
236 if 'basic' != authmeth.lower():
237 return self.build_authentication()
237 return self.build_authentication()
238 auth = auth.strip().decode('base64')
238 auth = auth.strip().decode('base64')
239 _parts = auth.split(':', 1)
239 _parts = auth.split(':', 1)
240 if len(_parts) == 2:
240 if len(_parts) == 2:
241 username, password = _parts
241 username, password = _parts
242 auth_data = self.authfunc(
242 auth_data = self.authfunc(
243 username, password, environ, VCS_TYPE,
243 username, password, environ, VCS_TYPE,
244 registry=self.registry, acl_repo_name=self.acl_repo_name)
244 registry=self.registry, acl_repo_name=self.acl_repo_name)
245 if auth_data:
245 if auth_data:
246 return {'username': username, 'auth_data': auth_data}
246 return {'username': username, 'auth_data': auth_data}
247 if username and password:
247 if username and password:
248 # we mark that we actually executed authentication once, at
248 # we mark that we actually executed authentication once, at
249 # that point we can use the alternative auth code
249 # that point we can use the alternative auth code
250 self.initial_call = False
250 self.initial_call = False
251
251
252 return self.build_authentication()
252 return self.build_authentication()
253
253
254 __call__ = authenticate
254 __call__ = authenticate
255
255
256
256
257 def calculate_version_hash(config):
257 def calculate_version_hash(config):
258 return md5(
258 return md5(
259 config.get('beaker.session.secret', '') +
259 config.get('beaker.session.secret', '') +
260 rhodecode.__version__)[:8]
260 rhodecode.__version__)[:8]
261
261
262
262
263 def get_current_lang(request):
263 def get_current_lang(request):
264 # NOTE(marcink): remove after pyramid move
264 # NOTE(marcink): remove after pyramid move
265 try:
265 try:
266 return translation.get_lang()[0]
266 return translation.get_lang()[0]
267 except:
267 except:
268 pass
268 pass
269
269
270 return getattr(request, '_LOCALE_', request.locale_name)
270 return getattr(request, '_LOCALE_', request.locale_name)
271
271
272
272
273 def attach_context_attributes(context, request, user_id):
273 def attach_context_attributes(context, request, user_id):
274 """
274 """
275 Attach variables into template context called `c`.
275 Attach variables into template context called `c`.
276 """
276 """
277 config = request.registry.settings
277 config = request.registry.settings
278
278
279
279
280 rc_config = SettingsModel().get_all_settings(cache=True)
280 rc_config = SettingsModel().get_all_settings(cache=True)
281
281
282 context.rhodecode_version = rhodecode.__version__
282 context.rhodecode_version = rhodecode.__version__
283 context.rhodecode_edition = config.get('rhodecode.edition')
283 context.rhodecode_edition = config.get('rhodecode.edition')
284 # unique secret + version does not leak the version but keep consistency
284 # unique secret + version does not leak the version but keep consistency
285 context.rhodecode_version_hash = calculate_version_hash(config)
285 context.rhodecode_version_hash = calculate_version_hash(config)
286
286
287 # Default language set for the incoming request
287 # Default language set for the incoming request
288 context.language = get_current_lang(request)
288 context.language = get_current_lang(request)
289
289
290 # Visual options
290 # Visual options
291 context.visual = AttributeDict({})
291 context.visual = AttributeDict({})
292
292
293 # DB stored Visual Items
293 # DB stored Visual Items
294 context.visual.show_public_icon = str2bool(
294 context.visual.show_public_icon = str2bool(
295 rc_config.get('rhodecode_show_public_icon'))
295 rc_config.get('rhodecode_show_public_icon'))
296 context.visual.show_private_icon = str2bool(
296 context.visual.show_private_icon = str2bool(
297 rc_config.get('rhodecode_show_private_icon'))
297 rc_config.get('rhodecode_show_private_icon'))
298 context.visual.stylify_metatags = str2bool(
298 context.visual.stylify_metatags = str2bool(
299 rc_config.get('rhodecode_stylify_metatags'))
299 rc_config.get('rhodecode_stylify_metatags'))
300 context.visual.dashboard_items = safe_int(
300 context.visual.dashboard_items = safe_int(
301 rc_config.get('rhodecode_dashboard_items', 100))
301 rc_config.get('rhodecode_dashboard_items', 100))
302 context.visual.admin_grid_items = safe_int(
302 context.visual.admin_grid_items = safe_int(
303 rc_config.get('rhodecode_admin_grid_items', 100))
303 rc_config.get('rhodecode_admin_grid_items', 100))
304 context.visual.repository_fields = str2bool(
304 context.visual.repository_fields = str2bool(
305 rc_config.get('rhodecode_repository_fields'))
305 rc_config.get('rhodecode_repository_fields'))
306 context.visual.show_version = str2bool(
306 context.visual.show_version = str2bool(
307 rc_config.get('rhodecode_show_version'))
307 rc_config.get('rhodecode_show_version'))
308 context.visual.use_gravatar = str2bool(
308 context.visual.use_gravatar = str2bool(
309 rc_config.get('rhodecode_use_gravatar'))
309 rc_config.get('rhodecode_use_gravatar'))
310 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
310 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
311 context.visual.default_renderer = rc_config.get(
311 context.visual.default_renderer = rc_config.get(
312 'rhodecode_markup_renderer', 'rst')
312 'rhodecode_markup_renderer', 'rst')
313 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
313 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
314 context.visual.rhodecode_support_url = \
314 context.visual.rhodecode_support_url = \
315 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
315 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
316
316
317 context.visual.affected_files_cut_off = 60
317 context.visual.affected_files_cut_off = 60
318
318
319 context.pre_code = rc_config.get('rhodecode_pre_code')
319 context.pre_code = rc_config.get('rhodecode_pre_code')
320 context.post_code = rc_config.get('rhodecode_post_code')
320 context.post_code = rc_config.get('rhodecode_post_code')
321 context.rhodecode_name = rc_config.get('rhodecode_title')
321 context.rhodecode_name = rc_config.get('rhodecode_title')
322 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
322 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
323 # if we have specified default_encoding in the request, it has more
323 # if we have specified default_encoding in the request, it has more
324 # priority
324 # priority
325 if request.GET.get('default_encoding'):
325 if request.GET.get('default_encoding'):
326 context.default_encodings.insert(0, request.GET.get('default_encoding'))
326 context.default_encodings.insert(0, request.GET.get('default_encoding'))
327 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
327 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
328
328
329 # INI stored
329 # INI stored
330 context.labs_active = str2bool(
330 context.labs_active = str2bool(
331 config.get('labs_settings_active', 'false'))
331 config.get('labs_settings_active', 'false'))
332 context.visual.allow_repo_location_change = str2bool(
332 context.visual.allow_repo_location_change = str2bool(
333 config.get('allow_repo_location_change', True))
333 config.get('allow_repo_location_change', True))
334 context.visual.allow_custom_hooks_settings = str2bool(
334 context.visual.allow_custom_hooks_settings = str2bool(
335 config.get('allow_custom_hooks_settings', True))
335 config.get('allow_custom_hooks_settings', True))
336 context.debug_style = str2bool(config.get('debug_style', False))
336 context.debug_style = str2bool(config.get('debug_style', False))
337
337
338 context.rhodecode_instanceid = config.get('instance_id')
338 context.rhodecode_instanceid = config.get('instance_id')
339
339
340 context.visual.cut_off_limit_diff = safe_int(
340 context.visual.cut_off_limit_diff = safe_int(
341 config.get('cut_off_limit_diff'))
341 config.get('cut_off_limit_diff'))
342 context.visual.cut_off_limit_file = safe_int(
342 context.visual.cut_off_limit_file = safe_int(
343 config.get('cut_off_limit_file'))
343 config.get('cut_off_limit_file'))
344
344
345 # AppEnlight
345 # AppEnlight
346 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
346 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
347 context.appenlight_api_public_key = config.get(
347 context.appenlight_api_public_key = config.get(
348 'appenlight.api_public_key', '')
348 'appenlight.api_public_key', '')
349 context.appenlight_server_url = config.get('appenlight.server_url', '')
349 context.appenlight_server_url = config.get('appenlight.server_url', '')
350
350
351 # JS template context
351 # JS template context
352 context.template_context = {
352 context.template_context = {
353 'repo_name': None,
353 'repo_name': None,
354 'repo_type': None,
354 'repo_type': None,
355 'repo_landing_commit': None,
355 'repo_landing_commit': None,
356 'rhodecode_user': {
356 'rhodecode_user': {
357 'username': None,
357 'username': None,
358 'email': None,
358 'email': None,
359 'notification_status': False
359 'notification_status': False
360 },
360 },
361 'visual': {
361 'visual': {
362 'default_renderer': None
362 'default_renderer': None
363 },
363 },
364 'commit_data': {
364 'commit_data': {
365 'commit_id': None
365 'commit_id': None
366 },
366 },
367 'pull_request_data': {'pull_request_id': None},
367 'pull_request_data': {'pull_request_id': None},
368 'timeago': {
368 'timeago': {
369 'refresh_time': 120 * 1000,
369 'refresh_time': 120 * 1000,
370 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
370 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
371 },
371 },
372 'pyramid_dispatch': {
372 'pyramid_dispatch': {
373
373
374 },
374 },
375 'extra': {'plugins': {}}
375 'extra': {'plugins': {}}
376 }
376 }
377 # END CONFIG VARS
377 # END CONFIG VARS
378
378
379 diffmode = 'sideside'
379 diffmode = 'sideside'
380 if request.GET.get('diffmode'):
380 if request.GET.get('diffmode'):
381 if request.GET['diffmode'] == 'unified':
381 if request.GET['diffmode'] == 'unified':
382 diffmode = 'unified'
382 diffmode = 'unified'
383 elif request.session.get('diffmode'):
383 elif request.session.get('diffmode'):
384 diffmode = request.session['diffmode']
384 diffmode = request.session['diffmode']
385
385
386 context.diffmode = diffmode
386 context.diffmode = diffmode
387
387
388 if request.session.get('diffmode') != diffmode:
388 if request.session.get('diffmode') != diffmode:
389 request.session['diffmode'] = diffmode
389 request.session['diffmode'] = diffmode
390
390
391 context.csrf_token = auth.get_csrf_token(session=request.session)
391 context.csrf_token = auth.get_csrf_token(session=request.session)
392 context.backends = rhodecode.BACKENDS.keys()
392 context.backends = rhodecode.BACKENDS.keys()
393 context.backends.sort()
393 context.backends.sort()
394 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
394 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
395
395
396 # web case
396 # web case
397 if hasattr(request, 'user'):
397 if hasattr(request, 'user'):
398 context.auth_user = request.user
398 context.auth_user = request.user
399 context.rhodecode_user = request.user
399 context.rhodecode_user = request.user
400
400
401 # api case
401 # api case
402 if hasattr(request, 'rpc_user'):
402 if hasattr(request, 'rpc_user'):
403 context.auth_user = request.rpc_user
403 context.auth_user = request.rpc_user
404 context.rhodecode_user = request.rpc_user
404 context.rhodecode_user = request.rpc_user
405
405
406 # attach the whole call context to the request
406 # attach the whole call context to the request
407 request.call_context = context
407 request.call_context = context
408
408
409
409
410 def get_auth_user(request):
410 def get_auth_user(request):
411 environ = request.environ
411 environ = request.environ
412 session = request.session
412 session = request.session
413
413
414 ip_addr = get_ip_addr(environ)
414 ip_addr = get_ip_addr(environ)
415 # make sure that we update permissions each time we call controller
415 # make sure that we update permissions each time we call controller
416 _auth_token = (request.GET.get('auth_token', '') or
416 _auth_token = (request.GET.get('auth_token', '') or
417 request.GET.get('api_key', ''))
417 request.GET.get('api_key', ''))
418
418
419 if _auth_token:
419 if _auth_token:
420 # when using API_KEY we assume user exists, and
420 # when using API_KEY we assume user exists, and
421 # doesn't need auth based on cookies.
421 # doesn't need auth based on cookies.
422 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
422 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
423 authenticated = False
423 authenticated = False
424 else:
424 else:
425 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
425 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
426 try:
426 try:
427 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
427 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
428 ip_addr=ip_addr)
428 ip_addr=ip_addr)
429 except UserCreationError as e:
429 except UserCreationError as e:
430 h.flash(e, 'error')
430 h.flash(e, 'error')
431 # container auth or other auth functions that create users
431 # container auth or other auth functions that create users
432 # on the fly can throw this exception signaling that there's
432 # on the fly can throw this exception signaling that there's
433 # issue with user creation, explanation should be provided
433 # issue with user creation, explanation should be provided
434 # in Exception itself. We then create a simple blank
434 # in Exception itself. We then create a simple blank
435 # AuthUser
435 # AuthUser
436 auth_user = AuthUser(ip_addr=ip_addr)
436 auth_user = AuthUser(ip_addr=ip_addr)
437
437
438 # in case someone changes a password for user it triggers session
438 # in case someone changes a password for user it triggers session
439 # flush and forces a re-login
439 # flush and forces a re-login
440 if password_changed(auth_user, session):
440 if password_changed(auth_user, session):
441 session.invalidate()
441 session.invalidate()
442 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
442 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
443 auth_user = AuthUser(ip_addr=ip_addr)
443 auth_user = AuthUser(ip_addr=ip_addr)
444
444
445 authenticated = cookie_store.get('is_authenticated')
445 authenticated = cookie_store.get('is_authenticated')
446
446
447 if not auth_user.is_authenticated and auth_user.is_user_object:
447 if not auth_user.is_authenticated and auth_user.is_user_object:
448 # user is not authenticated and not empty
448 # user is not authenticated and not empty
449 auth_user.set_authenticated(authenticated)
449 auth_user.set_authenticated(authenticated)
450
450
451 return auth_user
451 return auth_user
452
452
453
453
454 def h_filter(s):
454 def h_filter(s):
455 """
455 """
456 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
456 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
457 we wrap this with additional functionality that converts None to empty
457 we wrap this with additional functionality that converts None to empty
458 strings
458 strings
459 """
459 """
460 if s is None:
460 if s is None:
461 return markupsafe.Markup()
461 return markupsafe.Markup()
462 return markupsafe.escape(s)
462 return markupsafe.escape(s)
463
463
464
464
465 def add_events_routes(config):
465 def add_events_routes(config):
466 """
466 """
467 Adds routing that can be used in events. Because some events are triggered
467 Adds routing that can be used in events. Because some events are triggered
468 outside of pyramid context, we need to bootstrap request with some
468 outside of pyramid context, we need to bootstrap request with some
469 routing registered
469 routing registered
470 """
470 """
471
471
472 from rhodecode.apps._base import ADMIN_PREFIX
472 from rhodecode.apps._base import ADMIN_PREFIX
473
473
474 config.add_route(name='home', pattern='/')
474 config.add_route(name='home', pattern='/')
475
475
476 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
476 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
477 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
477 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
478 config.add_route(name='repo_summary', pattern='/{repo_name}')
478 config.add_route(name='repo_summary', pattern='/{repo_name}')
479 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
479 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
480 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
480 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
481
481
482 config.add_route(name='pullrequest_show',
482 config.add_route(name='pullrequest_show',
483 pattern='/{repo_name}/pull-request/{pull_request_id}')
483 pattern='/{repo_name}/pull-request/{pull_request_id}')
484 config.add_route(name='pull_requests_global',
484 config.add_route(name='pull_requests_global',
485 pattern='/pull-request/{pull_request_id}')
485 pattern='/pull-request/{pull_request_id}')
486 config.add_route(name='repo_commit',
486 config.add_route(name='repo_commit',
487 pattern='/{repo_name}/changeset/{commit_id}')
487 pattern='/{repo_name}/changeset/{commit_id}')
488
488
489 config.add_route(name='repo_files',
489 config.add_route(name='repo_files',
490 pattern='/{repo_name}/files/{commit_id}/{f_path}')
490 pattern='/{repo_name}/files/{commit_id}/{f_path}')
491
491
492
492
493 def bootstrap_config(request):
493 def bootstrap_config(request):
494 import pyramid.testing
494 import pyramid.testing
495 registry = pyramid.testing.Registry('RcTestRegistry')
495 registry = pyramid.testing.Registry('RcTestRegistry')
496
496
497 config = pyramid.testing.setUp(registry=registry, request=request)
497 config = pyramid.testing.setUp(registry=registry, request=request)
498
498
499 # allow pyramid lookup in testing
499 # allow pyramid lookup in testing
500 config.include('pyramid_mako')
500 config.include('pyramid_mako')
501 config.include('pyramid_beaker')
501 config.include('pyramid_beaker')
502 config.include('rhodecode.lib.caches')
502
503
503 add_events_routes(config)
504 add_events_routes(config)
504
505
505 return config
506 return config
506
507
507
508
508 def bootstrap_request(**kwargs):
509 def bootstrap_request(**kwargs):
509 import pyramid.testing
510 import pyramid.testing
510
511
511 class TestRequest(pyramid.testing.DummyRequest):
512 class TestRequest(pyramid.testing.DummyRequest):
512 application_url = kwargs.pop('application_url', 'http://example.com')
513 application_url = kwargs.pop('application_url', 'http://example.com')
513 host = kwargs.pop('host', 'example.com:80')
514 host = kwargs.pop('host', 'example.com:80')
514 domain = kwargs.pop('domain', 'example.com')
515 domain = kwargs.pop('domain', 'example.com')
515
516
516 def translate(self, msg):
517 def translate(self, msg):
517 return msg
518 return msg
518
519
519 def plularize(self, singular, plural, n):
520 def plularize(self, singular, plural, n):
520 return singular
521 return singular
521
522
522 def get_partial_renderer(self, tmpl_name):
523 def get_partial_renderer(self, tmpl_name):
523
524
524 from rhodecode.lib.partial_renderer import get_partial_renderer
525 from rhodecode.lib.partial_renderer import get_partial_renderer
525 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
526 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
526
527
527 _call_context = {}
528 _call_context = {}
528 @property
529 @property
529 def call_context(self):
530 def call_context(self):
530 return self._call_context
531 return self._call_context
531
532
532 class TestDummySession(pyramid.testing.DummySession):
533 class TestDummySession(pyramid.testing.DummySession):
533 def save(*arg, **kw):
534 def save(*arg, **kw):
534 pass
535 pass
535
536
536 request = TestRequest(**kwargs)
537 request = TestRequest(**kwargs)
537 request.session = TestDummySession()
538 request.session = TestDummySession()
538
539
539 return request
540 return request
540
541
@@ -1,235 +1,269 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2017 RhodeCode GmbH
3 # Copyright (C) 2015-2017 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 import beaker
22 import beaker
23 import logging
23 import logging
24 import threading
24 import threading
25
25
26 from beaker.cache import _cache_decorate, cache_regions, region_invalidate
26 from beaker.cache import _cache_decorate, cache_regions, region_invalidate
27 from sqlalchemy.exc import IntegrityError
27 from sqlalchemy.exc import IntegrityError
28
28
29 from rhodecode.lib.utils import safe_str, md5
29 from rhodecode.lib.utils import safe_str, md5
30 from rhodecode.model.db import Session, CacheKey
30 from rhodecode.model.db import Session, CacheKey
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34 FILE_TREE = 'cache_file_tree'
34 FILE_TREE = 'cache_file_tree'
35 FILE_TREE_META = 'cache_file_tree_metadata'
35 FILE_TREE_META = 'cache_file_tree_metadata'
36 FILE_SEARCH_TREE_META = 'cache_file_search_metadata'
36 FILE_SEARCH_TREE_META = 'cache_file_search_metadata'
37 SUMMARY_STATS = 'cache_summary_stats'
37 SUMMARY_STATS = 'cache_summary_stats'
38
38
39 # This list of caches gets purged when invalidation happens
39 # This list of caches gets purged when invalidation happens
40 USED_REPO_CACHES = (FILE_TREE, FILE_SEARCH_TREE_META)
40 USED_REPO_CACHES = (FILE_TREE, FILE_SEARCH_TREE_META)
41
41
42 DEFAULT_CACHE_MANAGER_CONFIG = {
42 DEFAULT_CACHE_MANAGER_CONFIG = {
43 'type': 'memorylru_base',
43 'type': 'memorylru_base',
44 'max_items': 10240,
44 'max_items': 10240,
45 'key_length': 256,
45 'key_length': 256,
46 'enabled': True
46 'enabled': True
47 }
47 }
48
48
49
49
50 def get_default_cache_settings(settings):
51 cache_settings = {}
52 for key in settings.keys():
53 for prefix in ['beaker.cache.', 'cache.']:
54 if key.startswith(prefix):
55 name = key.split(prefix)[1].strip()
56 cache_settings[name] = settings[key].strip()
57 return cache_settings
58
59
60 # set cache regions for beaker so celery can utilise it
61 def configure_caches(settings, default_region_settings=None):
62 cache_settings = {'regions': None}
63 # main cache settings used as default ...
64 cache_settings.update(get_default_cache_settings(settings))
65 default_region_settings = default_region_settings or \
66 {'type': DEFAULT_CACHE_MANAGER_CONFIG['type']}
67 if cache_settings['regions']:
68 for region in cache_settings['regions'].split(','):
69 region = region.strip()
70 region_settings = default_region_settings.copy()
71 for key, value in cache_settings.items():
72 if key.startswith(region):
73 region_settings[key.split('.')[1]] = value
74 log.debug('Configuring cache region `%s` with settings %s',
75 region, region_settings)
76 configure_cache_region(
77 region, region_settings, cache_settings)
78
79
50 def configure_cache_region(
80 def configure_cache_region(
51 region_name, region_kw, default_cache_kw, default_expire=60):
81 region_name, region_settings, default_cache_kw, default_expire=60):
52 default_type = default_cache_kw.get('type', 'memory')
82 default_type = default_cache_kw.get('type', 'memory')
53 default_lock_dir = default_cache_kw.get('lock_dir')
83 default_lock_dir = default_cache_kw.get('lock_dir')
54 default_data_dir = default_cache_kw.get('data_dir')
84 default_data_dir = default_cache_kw.get('data_dir')
55
85
56 region_kw['lock_dir'] = region_kw.get('lock_dir', default_lock_dir)
86 region_settings['lock_dir'] = region_settings.get('lock_dir', default_lock_dir)
57 region_kw['data_dir'] = region_kw.get('data_dir', default_data_dir)
87 region_settings['data_dir'] = region_settings.get('data_dir', default_data_dir)
58 region_kw['type'] = region_kw.get('type', default_type)
88 region_settings['type'] = region_settings.get('type', default_type)
59 region_kw['expire'] = int(region_kw.get('expire', default_expire))
89 region_settings['expire'] = int(region_settings.get('expire', default_expire))
60
90
61 beaker.cache.cache_regions[region_name] = region_kw
91 beaker.cache.cache_regions[region_name] = region_settings
62
92
63
93
64 def get_cache_manager(region_name, cache_name, custom_ttl=None):
94 def get_cache_manager(region_name, cache_name, custom_ttl=None):
65 """
95 """
66 Creates a Beaker cache manager. Such instance can be used like that::
96 Creates a Beaker cache manager. Such instance can be used like that::
67
97
68 _namespace = caches.get_repo_namespace_key(caches.XXX, repo_name)
98 _namespace = caches.get_repo_namespace_key(caches.XXX, repo_name)
69 cache_manager = caches.get_cache_manager('repo_cache_long', _namespace)
99 cache_manager = caches.get_cache_manager('repo_cache_long', _namespace)
70 _cache_key = caches.compute_key_from_params(repo_name, commit.raw_id)
100 _cache_key = caches.compute_key_from_params(repo_name, commit.raw_id)
71 def heavy_compute():
101 def heavy_compute():
72 ...
102 ...
73 result = cache_manager.get(_cache_key, createfunc=heavy_compute)
103 result = cache_manager.get(_cache_key, createfunc=heavy_compute)
74
104
75 :param region_name: region from ini file
105 :param region_name: region from ini file
76 :param cache_name: custom cache name, usually prefix+repo_name. eg
106 :param cache_name: custom cache name, usually prefix+repo_name. eg
77 file_switcher_repo1
107 file_switcher_repo1
78 :param custom_ttl: override .ini file timeout on this cache
108 :param custom_ttl: override .ini file timeout on this cache
79 :return: instance of cache manager
109 :return: instance of cache manager
80 """
110 """
81
111
82 cache_config = cache_regions.get(region_name, DEFAULT_CACHE_MANAGER_CONFIG)
112 cache_config = cache_regions.get(region_name, DEFAULT_CACHE_MANAGER_CONFIG)
83 if custom_ttl:
113 if custom_ttl:
84 log.debug('Updating region %s with custom ttl: %s',
114 log.debug('Updating region %s with custom ttl: %s',
85 region_name, custom_ttl)
115 region_name, custom_ttl)
86 cache_config.update({'expire': custom_ttl})
116 cache_config.update({'expire': custom_ttl})
87
117
88 return beaker.cache.Cache._get_cache(cache_name, cache_config)
118 return beaker.cache.Cache._get_cache(cache_name, cache_config)
89
119
90
120
91 def clear_cache_manager(cache_manager):
121 def clear_cache_manager(cache_manager):
92 """
122 """
93 namespace = 'foobar'
123 namespace = 'foobar'
94 cache_manager = get_cache_manager('repo_cache_long', namespace)
124 cache_manager = get_cache_manager('repo_cache_long', namespace)
95 clear_cache_manager(cache_manager)
125 clear_cache_manager(cache_manager)
96 """
126 """
97
127
98 log.debug('Clearing all values for cache manager %s', cache_manager)
128 log.debug('Clearing all values for cache manager %s', cache_manager)
99 cache_manager.clear()
129 cache_manager.clear()
100
130
101
131
102 def clear_repo_caches(repo_name):
132 def clear_repo_caches(repo_name):
103 # invalidate cache manager for this repo
133 # invalidate cache manager for this repo
104 for prefix in USED_REPO_CACHES:
134 for prefix in USED_REPO_CACHES:
105 namespace = get_repo_namespace_key(prefix, repo_name)
135 namespace = get_repo_namespace_key(prefix, repo_name)
106 cache_manager = get_cache_manager('repo_cache_long', namespace)
136 cache_manager = get_cache_manager('repo_cache_long', namespace)
107 clear_cache_manager(cache_manager)
137 clear_cache_manager(cache_manager)
108
138
109
139
110 def compute_key_from_params(*args):
140 def compute_key_from_params(*args):
111 """
141 """
112 Helper to compute key from given params to be used in cache manager
142 Helper to compute key from given params to be used in cache manager
113 """
143 """
114 return md5("_".join(map(safe_str, args)))
144 return md5("_".join(map(safe_str, args)))
115
145
116
146
117 def get_repo_namespace_key(prefix, repo_name):
147 def get_repo_namespace_key(prefix, repo_name):
118 return '{0}_{1}'.format(prefix, compute_key_from_params(repo_name))
148 return '{0}_{1}'.format(prefix, compute_key_from_params(repo_name))
119
149
120
150
121 def conditional_cache(region, prefix, condition, func):
151 def conditional_cache(region, prefix, condition, func):
122 """
152 """
123 Conditional caching function use like::
153 Conditional caching function use like::
124 def _c(arg):
154 def _c(arg):
125 # heavy computation function
155 # heavy computation function
126 return data
156 return data
127
157
128 # depending on the condition the compute is wrapped in cache or not
158 # depending on the condition the compute is wrapped in cache or not
129 compute = conditional_cache('short_term', 'cache_desc',
159 compute = conditional_cache('short_term', 'cache_desc',
130 condition=True, func=func)
160 condition=True, func=func)
131 return compute(arg)
161 return compute(arg)
132
162
133 :param region: name of cache region
163 :param region: name of cache region
134 :param prefix: cache region prefix
164 :param prefix: cache region prefix
135 :param condition: condition for cache to be triggered, and
165 :param condition: condition for cache to be triggered, and
136 return data cached
166 return data cached
137 :param func: wrapped heavy function to compute
167 :param func: wrapped heavy function to compute
138
168
139 """
169 """
140 wrapped = func
170 wrapped = func
141 if condition:
171 if condition:
142 log.debug('conditional_cache: True, wrapping call of '
172 log.debug('conditional_cache: True, wrapping call of '
143 'func: %s into %s region cache', region, func)
173 'func: %s into %s region cache', region, func)
144 cached_region = _cache_decorate((prefix,), None, None, region)
174 cached_region = _cache_decorate((prefix,), None, None, region)
145 wrapped = cached_region(func)
175 wrapped = cached_region(func)
146 return wrapped
176 return wrapped
147
177
148
178
149 class ActiveRegionCache(object):
179 class ActiveRegionCache(object):
150 def __init__(self, context):
180 def __init__(self, context):
151 self.context = context
181 self.context = context
152
182
153 def invalidate(self, *args, **kwargs):
183 def invalidate(self, *args, **kwargs):
154 return False
184 return False
155
185
156 def compute(self):
186 def compute(self):
157 log.debug('Context cache: getting obj %s from cache', self.context)
187 log.debug('Context cache: getting obj %s from cache', self.context)
158 return self.context.compute_func(self.context.cache_key)
188 return self.context.compute_func(self.context.cache_key)
159
189
160
190
161 class FreshRegionCache(ActiveRegionCache):
191 class FreshRegionCache(ActiveRegionCache):
162 def invalidate(self):
192 def invalidate(self):
163 log.debug('Context cache: invalidating cache for %s', self.context)
193 log.debug('Context cache: invalidating cache for %s', self.context)
164 region_invalidate(
194 region_invalidate(
165 self.context.compute_func, None, self.context.cache_key)
195 self.context.compute_func, None, self.context.cache_key)
166 return True
196 return True
167
197
168
198
169 class InvalidationContext(object):
199 class InvalidationContext(object):
170 def __repr__(self):
200 def __repr__(self):
171 return '<InvalidationContext:{}[{}]>'.format(
201 return '<InvalidationContext:{}[{}]>'.format(
172 safe_str(self.repo_name), safe_str(self.cache_type))
202 safe_str(self.repo_name), safe_str(self.cache_type))
173
203
174 def __init__(self, compute_func, repo_name, cache_type,
204 def __init__(self, compute_func, repo_name, cache_type,
175 raise_exception=False, thread_scoped=False):
205 raise_exception=False, thread_scoped=False):
176 self.compute_func = compute_func
206 self.compute_func = compute_func
177 self.repo_name = repo_name
207 self.repo_name = repo_name
178 self.cache_type = cache_type
208 self.cache_type = cache_type
179 self.cache_key = compute_key_from_params(
209 self.cache_key = compute_key_from_params(
180 repo_name, cache_type)
210 repo_name, cache_type)
181 self.raise_exception = raise_exception
211 self.raise_exception = raise_exception
182
212
183 # Append the thread id to the cache key if this invalidation context
213 # Append the thread id to the cache key if this invalidation context
184 # should be scoped to the current thread.
214 # should be scoped to the current thread.
185 if thread_scoped:
215 if thread_scoped:
186 thread_id = threading.current_thread().ident
216 thread_id = threading.current_thread().ident
187 self.cache_key = '{cache_key}_{thread_id}'.format(
217 self.cache_key = '{cache_key}_{thread_id}'.format(
188 cache_key=self.cache_key, thread_id=thread_id)
218 cache_key=self.cache_key, thread_id=thread_id)
189
219
190 def get_cache_obj(self):
220 def get_cache_obj(self):
191 cache_key = CacheKey.get_cache_key(
221 cache_key = CacheKey.get_cache_key(
192 self.repo_name, self.cache_type)
222 self.repo_name, self.cache_type)
193 cache_obj = CacheKey.get_active_cache(cache_key)
223 cache_obj = CacheKey.get_active_cache(cache_key)
194 if not cache_obj:
224 if not cache_obj:
195 cache_obj = CacheKey(cache_key, self.repo_name)
225 cache_obj = CacheKey(cache_key, self.repo_name)
196 return cache_obj
226 return cache_obj
197
227
198 def __enter__(self):
228 def __enter__(self):
199 """
229 """
200 Test if current object is valid, and return CacheRegion function
230 Test if current object is valid, and return CacheRegion function
201 that does invalidation and calculation
231 that does invalidation and calculation
202 """
232 """
203
233
204 self.cache_obj = self.get_cache_obj()
234 self.cache_obj = self.get_cache_obj()
205 if self.cache_obj.cache_active:
235 if self.cache_obj.cache_active:
206 # means our cache obj is existing and marked as it's
236 # means our cache obj is existing and marked as it's
207 # cache is not outdated, we return BaseInvalidator
237 # cache is not outdated, we return BaseInvalidator
208 self.skip_cache_active_change = True
238 self.skip_cache_active_change = True
209 return ActiveRegionCache(self)
239 return ActiveRegionCache(self)
210
240
211 # the key is either not existing or set to False, we return
241 # the key is either not existing or set to False, we return
212 # the real invalidator which re-computes value. We additionally set
242 # the real invalidator which re-computes value. We additionally set
213 # the flag to actually update the Database objects
243 # the flag to actually update the Database objects
214 self.skip_cache_active_change = False
244 self.skip_cache_active_change = False
215 return FreshRegionCache(self)
245 return FreshRegionCache(self)
216
246
217 def __exit__(self, exc_type, exc_val, exc_tb):
247 def __exit__(self, exc_type, exc_val, exc_tb):
218
248
219 if self.skip_cache_active_change:
249 if self.skip_cache_active_change:
220 return
250 return
221
251
222 try:
252 try:
223 self.cache_obj.cache_active = True
253 self.cache_obj.cache_active = True
224 Session().add(self.cache_obj)
254 Session().add(self.cache_obj)
225 Session().commit()
255 Session().commit()
226 except IntegrityError:
256 except IntegrityError:
227 # if we catch integrity error, it means we inserted this object
257 # if we catch integrity error, it means we inserted this object
228 # assumption is that's really an edge race-condition case and
258 # assumption is that's really an edge race-condition case and
229 # it's safe is to skip it
259 # it's safe is to skip it
230 Session().rollback()
260 Session().rollback()
231 except Exception:
261 except Exception:
232 log.exception('Failed to commit on cache key update')
262 log.exception('Failed to commit on cache key update')
233 Session().rollback()
263 Session().rollback()
234 if self.raise_exception:
264 if self.raise_exception:
235 raise
265 raise
266
267
268 def includeme(config):
269 configure_caches(config.registry.settings)
@@ -1,802 +1,773 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 Utilities library for RhodeCode
22 Utilities library for RhodeCode
23 """
23 """
24
24
25 import datetime
25 import datetime
26 import decorator
26 import decorator
27 import json
27 import json
28 import logging
28 import logging
29 import os
29 import os
30 import re
30 import re
31 import shutil
31 import shutil
32 import tempfile
32 import tempfile
33 import traceback
33 import traceback
34 import tarfile
34 import tarfile
35 import warnings
35 import warnings
36 import hashlib
36 import hashlib
37 from os.path import join as jn
37 from os.path import join as jn
38
38
39 import paste
39 import paste
40 import pkg_resources
40 import pkg_resources
41 from webhelpers.text import collapse, remove_formatting, strip_tags
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from mako import exceptions
42 from mako import exceptions
43 from pyramid.threadlocal import get_current_registry
43 from pyramid.threadlocal import get_current_registry
44 from pyramid.request import Request
44 from pyramid.request import Request
45
45
46 from rhodecode.lib.fakemod import create_module
46 from rhodecode.lib.fakemod import create_module
47 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.backends.base import Config
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
50 from rhodecode.lib.utils2 import (
50 from rhodecode.lib.utils2 import (
51 safe_str, safe_unicode, get_current_rhodecode_user, md5)
51 safe_str, safe_unicode, get_current_rhodecode_user, md5)
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import (
53 from rhodecode.model.db import (
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56
56
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61
61
62 # String which contains characters that are not allowed in slug names for
62 # String which contains characters that are not allowed in slug names for
63 # repositories or repository groups. It is properly escaped to use it in
63 # repositories or repository groups. It is properly escaped to use it in
64 # regular expressions.
64 # regular expressions.
65 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
65 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66
66
67 # Regex that matches forbidden characters in repo/group slugs.
67 # Regex that matches forbidden characters in repo/group slugs.
68 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
68 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
69
69
70 # Regex that matches allowed characters in repo/group slugs.
70 # Regex that matches allowed characters in repo/group slugs.
71 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
71 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
72
72
73 # Regex that matches whole repo/group slugs.
73 # Regex that matches whole repo/group slugs.
74 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
74 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
75
75
76 _license_cache = None
76 _license_cache = None
77
77
78
78
79 def repo_name_slug(value):
79 def repo_name_slug(value):
80 """
80 """
81 Return slug of name of repository
81 Return slug of name of repository
82 This function is called on each creation/modification
82 This function is called on each creation/modification
83 of repository to prevent bad names in repo
83 of repository to prevent bad names in repo
84 """
84 """
85 replacement_char = '-'
85 replacement_char = '-'
86
86
87 slug = remove_formatting(value)
87 slug = remove_formatting(value)
88 slug = SLUG_BAD_CHAR_RE.sub('', slug)
88 slug = SLUG_BAD_CHAR_RE.sub('', slug)
89 slug = re.sub('[\s]+', '-', slug)
89 slug = re.sub('[\s]+', '-', slug)
90 slug = collapse(slug, replacement_char)
90 slug = collapse(slug, replacement_char)
91 return slug
91 return slug
92
92
93
93
94 #==============================================================================
94 #==============================================================================
95 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
95 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
96 #==============================================================================
96 #==============================================================================
97 def get_repo_slug(request):
97 def get_repo_slug(request):
98 _repo = ''
98 _repo = ''
99
99
100 if hasattr(request, 'db_repo'):
100 if hasattr(request, 'db_repo'):
101 # if our requests has set db reference use it for name, this
101 # if our requests has set db reference use it for name, this
102 # translates the example.com/_<id> into proper repo names
102 # translates the example.com/_<id> into proper repo names
103 _repo = request.db_repo.repo_name
103 _repo = request.db_repo.repo_name
104 elif getattr(request, 'matchdict', None):
104 elif getattr(request, 'matchdict', None):
105 # pyramid
105 # pyramid
106 _repo = request.matchdict.get('repo_name')
106 _repo = request.matchdict.get('repo_name')
107
107
108 if _repo:
108 if _repo:
109 _repo = _repo.rstrip('/')
109 _repo = _repo.rstrip('/')
110 return _repo
110 return _repo
111
111
112
112
113 def get_repo_group_slug(request):
113 def get_repo_group_slug(request):
114 _group = ''
114 _group = ''
115 if hasattr(request, 'db_repo_group'):
115 if hasattr(request, 'db_repo_group'):
116 # if our requests has set db reference use it for name, this
116 # if our requests has set db reference use it for name, this
117 # translates the example.com/_<id> into proper repo group names
117 # translates the example.com/_<id> into proper repo group names
118 _group = request.db_repo_group.group_name
118 _group = request.db_repo_group.group_name
119 elif getattr(request, 'matchdict', None):
119 elif getattr(request, 'matchdict', None):
120 # pyramid
120 # pyramid
121 _group = request.matchdict.get('repo_group_name')
121 _group = request.matchdict.get('repo_group_name')
122
122
123
123
124 if _group:
124 if _group:
125 _group = _group.rstrip('/')
125 _group = _group.rstrip('/')
126 return _group
126 return _group
127
127
128
128
129 def get_user_group_slug(request):
129 def get_user_group_slug(request):
130 _user_group = ''
130 _user_group = ''
131
131
132 if hasattr(request, 'db_user_group'):
132 if hasattr(request, 'db_user_group'):
133 _user_group = request.db_user_group.users_group_name
133 _user_group = request.db_user_group.users_group_name
134 elif getattr(request, 'matchdict', None):
134 elif getattr(request, 'matchdict', None):
135 # pyramid
135 # pyramid
136 _user_group = request.matchdict.get('user_group_id')
136 _user_group = request.matchdict.get('user_group_id')
137
137
138 try:
138 try:
139 _user_group = UserGroup.get(_user_group)
139 _user_group = UserGroup.get(_user_group)
140 if _user_group:
140 if _user_group:
141 _user_group = _user_group.users_group_name
141 _user_group = _user_group.users_group_name
142 except Exception:
142 except Exception:
143 log.exception('Failed to get user group by id')
143 log.exception('Failed to get user group by id')
144 # catch all failures here
144 # catch all failures here
145 return None
145 return None
146
146
147 return _user_group
147 return _user_group
148
148
149
149
150 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
150 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
151 """
151 """
152 Scans given path for repos and return (name,(type,path)) tuple
152 Scans given path for repos and return (name,(type,path)) tuple
153
153
154 :param path: path to scan for repositories
154 :param path: path to scan for repositories
155 :param recursive: recursive search and return names with subdirs in front
155 :param recursive: recursive search and return names with subdirs in front
156 """
156 """
157
157
158 # remove ending slash for better results
158 # remove ending slash for better results
159 path = path.rstrip(os.sep)
159 path = path.rstrip(os.sep)
160 log.debug('now scanning in %s location recursive:%s...', path, recursive)
160 log.debug('now scanning in %s location recursive:%s...', path, recursive)
161
161
162 def _get_repos(p):
162 def _get_repos(p):
163 dirpaths = _get_dirpaths(p)
163 dirpaths = _get_dirpaths(p)
164 if not _is_dir_writable(p):
164 if not _is_dir_writable(p):
165 log.warning('repo path without write access: %s', p)
165 log.warning('repo path without write access: %s', p)
166
166
167 for dirpath in dirpaths:
167 for dirpath in dirpaths:
168 if os.path.isfile(os.path.join(p, dirpath)):
168 if os.path.isfile(os.path.join(p, dirpath)):
169 continue
169 continue
170 cur_path = os.path.join(p, dirpath)
170 cur_path = os.path.join(p, dirpath)
171
171
172 # skip removed repos
172 # skip removed repos
173 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
173 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
174 continue
174 continue
175
175
176 #skip .<somethin> dirs
176 #skip .<somethin> dirs
177 if dirpath.startswith('.'):
177 if dirpath.startswith('.'):
178 continue
178 continue
179
179
180 try:
180 try:
181 scm_info = get_scm(cur_path)
181 scm_info = get_scm(cur_path)
182 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
182 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
183 except VCSError:
183 except VCSError:
184 if not recursive:
184 if not recursive:
185 continue
185 continue
186 #check if this dir containts other repos for recursive scan
186 #check if this dir containts other repos for recursive scan
187 rec_path = os.path.join(p, dirpath)
187 rec_path = os.path.join(p, dirpath)
188 if os.path.isdir(rec_path):
188 if os.path.isdir(rec_path):
189 for inner_scm in _get_repos(rec_path):
189 for inner_scm in _get_repos(rec_path):
190 yield inner_scm
190 yield inner_scm
191
191
192 return _get_repos(path)
192 return _get_repos(path)
193
193
194
194
195 def _get_dirpaths(p):
195 def _get_dirpaths(p):
196 try:
196 try:
197 # OS-independable way of checking if we have at least read-only
197 # OS-independable way of checking if we have at least read-only
198 # access or not.
198 # access or not.
199 dirpaths = os.listdir(p)
199 dirpaths = os.listdir(p)
200 except OSError:
200 except OSError:
201 log.warning('ignoring repo path without read access: %s', p)
201 log.warning('ignoring repo path without read access: %s', p)
202 return []
202 return []
203
203
204 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
204 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
205 # decode paths and suddenly returns unicode objects itself. The items it
205 # decode paths and suddenly returns unicode objects itself. The items it
206 # cannot decode are returned as strings and cause issues.
206 # cannot decode are returned as strings and cause issues.
207 #
207 #
208 # Those paths are ignored here until a solid solution for path handling has
208 # Those paths are ignored here until a solid solution for path handling has
209 # been built.
209 # been built.
210 expected_type = type(p)
210 expected_type = type(p)
211
211
212 def _has_correct_type(item):
212 def _has_correct_type(item):
213 if type(item) is not expected_type:
213 if type(item) is not expected_type:
214 log.error(
214 log.error(
215 u"Ignoring path %s since it cannot be decoded into unicode.",
215 u"Ignoring path %s since it cannot be decoded into unicode.",
216 # Using "repr" to make sure that we see the byte value in case
216 # Using "repr" to make sure that we see the byte value in case
217 # of support.
217 # of support.
218 repr(item))
218 repr(item))
219 return False
219 return False
220 return True
220 return True
221
221
222 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
222 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
223
223
224 return dirpaths
224 return dirpaths
225
225
226
226
227 def _is_dir_writable(path):
227 def _is_dir_writable(path):
228 """
228 """
229 Probe if `path` is writable.
229 Probe if `path` is writable.
230
230
231 Due to trouble on Cygwin / Windows, this is actually probing if it is
231 Due to trouble on Cygwin / Windows, this is actually probing if it is
232 possible to create a file inside of `path`, stat does not produce reliable
232 possible to create a file inside of `path`, stat does not produce reliable
233 results in this case.
233 results in this case.
234 """
234 """
235 try:
235 try:
236 with tempfile.TemporaryFile(dir=path):
236 with tempfile.TemporaryFile(dir=path):
237 pass
237 pass
238 except OSError:
238 except OSError:
239 return False
239 return False
240 return True
240 return True
241
241
242
242
243 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
243 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
244 """
244 """
245 Returns True if given path is a valid repository False otherwise.
245 Returns True if given path is a valid repository False otherwise.
246 If expect_scm param is given also, compare if given scm is the same
246 If expect_scm param is given also, compare if given scm is the same
247 as expected from scm parameter. If explicit_scm is given don't try to
247 as expected from scm parameter. If explicit_scm is given don't try to
248 detect the scm, just use the given one to check if repo is valid
248 detect the scm, just use the given one to check if repo is valid
249
249
250 :param repo_name:
250 :param repo_name:
251 :param base_path:
251 :param base_path:
252 :param expect_scm:
252 :param expect_scm:
253 :param explicit_scm:
253 :param explicit_scm:
254
254
255 :return True: if given path is a valid repository
255 :return True: if given path is a valid repository
256 """
256 """
257 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
257 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
258 log.debug('Checking if `%s` is a valid path for repository. '
258 log.debug('Checking if `%s` is a valid path for repository. '
259 'Explicit type: %s', repo_name, explicit_scm)
259 'Explicit type: %s', repo_name, explicit_scm)
260
260
261 try:
261 try:
262 if explicit_scm:
262 if explicit_scm:
263 detected_scms = [get_scm_backend(explicit_scm)(full_path).alias]
263 detected_scms = [get_scm_backend(explicit_scm)(full_path).alias]
264 else:
264 else:
265 detected_scms = get_scm(full_path)
265 detected_scms = get_scm(full_path)
266
266
267 if expect_scm:
267 if expect_scm:
268 return detected_scms[0] == expect_scm
268 return detected_scms[0] == expect_scm
269 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
269 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
270 return True
270 return True
271 except VCSError:
271 except VCSError:
272 log.debug('path: %s is not a valid repo !', full_path)
272 log.debug('path: %s is not a valid repo !', full_path)
273 return False
273 return False
274
274
275
275
276 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
276 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
277 """
277 """
278 Returns True if given path is a repository group, False otherwise
278 Returns True if given path is a repository group, False otherwise
279
279
280 :param repo_name:
280 :param repo_name:
281 :param base_path:
281 :param base_path:
282 """
282 """
283 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
283 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
284 log.debug('Checking if `%s` is a valid path for repository group',
284 log.debug('Checking if `%s` is a valid path for repository group',
285 repo_group_name)
285 repo_group_name)
286
286
287 # check if it's not a repo
287 # check if it's not a repo
288 if is_valid_repo(repo_group_name, base_path):
288 if is_valid_repo(repo_group_name, base_path):
289 log.debug('Repo called %s exist, it is not a valid '
289 log.debug('Repo called %s exist, it is not a valid '
290 'repo group' % repo_group_name)
290 'repo group' % repo_group_name)
291 return False
291 return False
292
292
293 try:
293 try:
294 # we need to check bare git repos at higher level
294 # we need to check bare git repos at higher level
295 # since we might match branches/hooks/info/objects or possible
295 # since we might match branches/hooks/info/objects or possible
296 # other things inside bare git repo
296 # other things inside bare git repo
297 scm_ = get_scm(os.path.dirname(full_path))
297 scm_ = get_scm(os.path.dirname(full_path))
298 log.debug('path: %s is a vcs object:%s, not valid '
298 log.debug('path: %s is a vcs object:%s, not valid '
299 'repo group' % (full_path, scm_))
299 'repo group' % (full_path, scm_))
300 return False
300 return False
301 except VCSError:
301 except VCSError:
302 pass
302 pass
303
303
304 # check if it's a valid path
304 # check if it's a valid path
305 if skip_path_check or os.path.isdir(full_path):
305 if skip_path_check or os.path.isdir(full_path):
306 log.debug('path: %s is a valid repo group !', full_path)
306 log.debug('path: %s is a valid repo group !', full_path)
307 return True
307 return True
308
308
309 log.debug('path: %s is not a valid repo group !', full_path)
309 log.debug('path: %s is not a valid repo group !', full_path)
310 return False
310 return False
311
311
312
312
313 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
313 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
314 while True:
314 while True:
315 ok = raw_input(prompt)
315 ok = raw_input(prompt)
316 if ok.lower() in ('y', 'ye', 'yes'):
316 if ok.lower() in ('y', 'ye', 'yes'):
317 return True
317 return True
318 if ok.lower() in ('n', 'no', 'nop', 'nope'):
318 if ok.lower() in ('n', 'no', 'nop', 'nope'):
319 return False
319 return False
320 retries = retries - 1
320 retries = retries - 1
321 if retries < 0:
321 if retries < 0:
322 raise IOError
322 raise IOError
323 print(complaint)
323 print(complaint)
324
324
325 # propagated from mercurial documentation
325 # propagated from mercurial documentation
326 ui_sections = [
326 ui_sections = [
327 'alias', 'auth',
327 'alias', 'auth',
328 'decode/encode', 'defaults',
328 'decode/encode', 'defaults',
329 'diff', 'email',
329 'diff', 'email',
330 'extensions', 'format',
330 'extensions', 'format',
331 'merge-patterns', 'merge-tools',
331 'merge-patterns', 'merge-tools',
332 'hooks', 'http_proxy',
332 'hooks', 'http_proxy',
333 'smtp', 'patch',
333 'smtp', 'patch',
334 'paths', 'profiling',
334 'paths', 'profiling',
335 'server', 'trusted',
335 'server', 'trusted',
336 'ui', 'web', ]
336 'ui', 'web', ]
337
337
338
338
339 def config_data_from_db(clear_session=True, repo=None):
339 def config_data_from_db(clear_session=True, repo=None):
340 """
340 """
341 Read the configuration data from the database and return configuration
341 Read the configuration data from the database and return configuration
342 tuples.
342 tuples.
343 """
343 """
344 from rhodecode.model.settings import VcsSettingsModel
344 from rhodecode.model.settings import VcsSettingsModel
345
345
346 config = []
346 config = []
347
347
348 sa = meta.Session()
348 sa = meta.Session()
349 settings_model = VcsSettingsModel(repo=repo, sa=sa)
349 settings_model = VcsSettingsModel(repo=repo, sa=sa)
350
350
351 ui_settings = settings_model.get_ui_settings()
351 ui_settings = settings_model.get_ui_settings()
352
352
353 for setting in ui_settings:
353 for setting in ui_settings:
354 if setting.active:
354 if setting.active:
355 log.debug(
355 log.debug(
356 'settings ui from db: [%s] %s=%s',
356 'settings ui from db: [%s] %s=%s',
357 setting.section, setting.key, setting.value)
357 setting.section, setting.key, setting.value)
358 config.append((
358 config.append((
359 safe_str(setting.section), safe_str(setting.key),
359 safe_str(setting.section), safe_str(setting.key),
360 safe_str(setting.value)))
360 safe_str(setting.value)))
361 if setting.key == 'push_ssl':
361 if setting.key == 'push_ssl':
362 # force set push_ssl requirement to False, rhodecode
362 # force set push_ssl requirement to False, rhodecode
363 # handles that
363 # handles that
364 config.append((
364 config.append((
365 safe_str(setting.section), safe_str(setting.key), False))
365 safe_str(setting.section), safe_str(setting.key), False))
366 if clear_session:
366 if clear_session:
367 meta.Session.remove()
367 meta.Session.remove()
368
368
369 # TODO: mikhail: probably it makes no sense to re-read hooks information.
369 # TODO: mikhail: probably it makes no sense to re-read hooks information.
370 # It's already there and activated/deactivated
370 # It's already there and activated/deactivated
371 skip_entries = []
371 skip_entries = []
372 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
372 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
373 if 'pull' not in enabled_hook_classes:
373 if 'pull' not in enabled_hook_classes:
374 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
374 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
375 if 'push' not in enabled_hook_classes:
375 if 'push' not in enabled_hook_classes:
376 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
376 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
377 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
377 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
378 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
378 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
379
379
380 config = [entry for entry in config if entry[:2] not in skip_entries]
380 config = [entry for entry in config if entry[:2] not in skip_entries]
381
381
382 return config
382 return config
383
383
384
384
385 def make_db_config(clear_session=True, repo=None):
385 def make_db_config(clear_session=True, repo=None):
386 """
386 """
387 Create a :class:`Config` instance based on the values in the database.
387 Create a :class:`Config` instance based on the values in the database.
388 """
388 """
389 config = Config()
389 config = Config()
390 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
390 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
391 for section, option, value in config_data:
391 for section, option, value in config_data:
392 config.set(section, option, value)
392 config.set(section, option, value)
393 return config
393 return config
394
394
395
395
396 def get_enabled_hook_classes(ui_settings):
396 def get_enabled_hook_classes(ui_settings):
397 """
397 """
398 Return the enabled hook classes.
398 Return the enabled hook classes.
399
399
400 :param ui_settings: List of ui_settings as returned
400 :param ui_settings: List of ui_settings as returned
401 by :meth:`VcsSettingsModel.get_ui_settings`
401 by :meth:`VcsSettingsModel.get_ui_settings`
402
402
403 :return: a list with the enabled hook classes. The order is not guaranteed.
403 :return: a list with the enabled hook classes. The order is not guaranteed.
404 :rtype: list
404 :rtype: list
405 """
405 """
406 enabled_hooks = []
406 enabled_hooks = []
407 active_hook_keys = [
407 active_hook_keys = [
408 key for section, key, value, active in ui_settings
408 key for section, key, value, active in ui_settings
409 if section == 'hooks' and active]
409 if section == 'hooks' and active]
410
410
411 hook_names = {
411 hook_names = {
412 RhodeCodeUi.HOOK_PUSH: 'push',
412 RhodeCodeUi.HOOK_PUSH: 'push',
413 RhodeCodeUi.HOOK_PULL: 'pull',
413 RhodeCodeUi.HOOK_PULL: 'pull',
414 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
414 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
415 }
415 }
416
416
417 for key in active_hook_keys:
417 for key in active_hook_keys:
418 hook = hook_names.get(key)
418 hook = hook_names.get(key)
419 if hook:
419 if hook:
420 enabled_hooks.append(hook)
420 enabled_hooks.append(hook)
421
421
422 return enabled_hooks
422 return enabled_hooks
423
423
424
424
425 def set_rhodecode_config(config):
425 def set_rhodecode_config(config):
426 """
426 """
427 Updates pyramid config with new settings from database
427 Updates pyramid config with new settings from database
428
428
429 :param config:
429 :param config:
430 """
430 """
431 from rhodecode.model.settings import SettingsModel
431 from rhodecode.model.settings import SettingsModel
432 app_settings = SettingsModel().get_all_settings()
432 app_settings = SettingsModel().get_all_settings()
433
433
434 for k, v in app_settings.items():
434 for k, v in app_settings.items():
435 config[k] = v
435 config[k] = v
436
436
437
437
438 def get_rhodecode_realm():
438 def get_rhodecode_realm():
439 """
439 """
440 Return the rhodecode realm from database.
440 Return the rhodecode realm from database.
441 """
441 """
442 from rhodecode.model.settings import SettingsModel
442 from rhodecode.model.settings import SettingsModel
443 realm = SettingsModel().get_setting_by_name('realm')
443 realm = SettingsModel().get_setting_by_name('realm')
444 return safe_str(realm.app_settings_value)
444 return safe_str(realm.app_settings_value)
445
445
446
446
447 def get_rhodecode_base_path():
447 def get_rhodecode_base_path():
448 """
448 """
449 Returns the base path. The base path is the filesystem path which points
449 Returns the base path. The base path is the filesystem path which points
450 to the repository store.
450 to the repository store.
451 """
451 """
452 from rhodecode.model.settings import SettingsModel
452 from rhodecode.model.settings import SettingsModel
453 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
453 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
454 return safe_str(paths_ui.ui_value)
454 return safe_str(paths_ui.ui_value)
455
455
456
456
457 def map_groups(path):
457 def map_groups(path):
458 """
458 """
459 Given a full path to a repository, create all nested groups that this
459 Given a full path to a repository, create all nested groups that this
460 repo is inside. This function creates parent-child relationships between
460 repo is inside. This function creates parent-child relationships between
461 groups and creates default perms for all new groups.
461 groups and creates default perms for all new groups.
462
462
463 :param paths: full path to repository
463 :param paths: full path to repository
464 """
464 """
465 from rhodecode.model.repo_group import RepoGroupModel
465 from rhodecode.model.repo_group import RepoGroupModel
466 sa = meta.Session()
466 sa = meta.Session()
467 groups = path.split(Repository.NAME_SEP)
467 groups = path.split(Repository.NAME_SEP)
468 parent = None
468 parent = None
469 group = None
469 group = None
470
470
471 # last element is repo in nested groups structure
471 # last element is repo in nested groups structure
472 groups = groups[:-1]
472 groups = groups[:-1]
473 rgm = RepoGroupModel(sa)
473 rgm = RepoGroupModel(sa)
474 owner = User.get_first_super_admin()
474 owner = User.get_first_super_admin()
475 for lvl, group_name in enumerate(groups):
475 for lvl, group_name in enumerate(groups):
476 group_name = '/'.join(groups[:lvl] + [group_name])
476 group_name = '/'.join(groups[:lvl] + [group_name])
477 group = RepoGroup.get_by_group_name(group_name)
477 group = RepoGroup.get_by_group_name(group_name)
478 desc = '%s group' % group_name
478 desc = '%s group' % group_name
479
479
480 # skip folders that are now removed repos
480 # skip folders that are now removed repos
481 if REMOVED_REPO_PAT.match(group_name):
481 if REMOVED_REPO_PAT.match(group_name):
482 break
482 break
483
483
484 if group is None:
484 if group is None:
485 log.debug('creating group level: %s group_name: %s',
485 log.debug('creating group level: %s group_name: %s',
486 lvl, group_name)
486 lvl, group_name)
487 group = RepoGroup(group_name, parent)
487 group = RepoGroup(group_name, parent)
488 group.group_description = desc
488 group.group_description = desc
489 group.user = owner
489 group.user = owner
490 sa.add(group)
490 sa.add(group)
491 perm_obj = rgm._create_default_perms(group)
491 perm_obj = rgm._create_default_perms(group)
492 sa.add(perm_obj)
492 sa.add(perm_obj)
493 sa.flush()
493 sa.flush()
494
494
495 parent = group
495 parent = group
496 return group
496 return group
497
497
498
498
499 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
499 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
500 """
500 """
501 maps all repos given in initial_repo_list, non existing repositories
501 maps all repos given in initial_repo_list, non existing repositories
502 are created, if remove_obsolete is True it also checks for db entries
502 are created, if remove_obsolete is True it also checks for db entries
503 that are not in initial_repo_list and removes them.
503 that are not in initial_repo_list and removes them.
504
504
505 :param initial_repo_list: list of repositories found by scanning methods
505 :param initial_repo_list: list of repositories found by scanning methods
506 :param remove_obsolete: check for obsolete entries in database
506 :param remove_obsolete: check for obsolete entries in database
507 """
507 """
508 from rhodecode.model.repo import RepoModel
508 from rhodecode.model.repo import RepoModel
509 from rhodecode.model.scm import ScmModel
509 from rhodecode.model.scm import ScmModel
510 from rhodecode.model.repo_group import RepoGroupModel
510 from rhodecode.model.repo_group import RepoGroupModel
511 from rhodecode.model.settings import SettingsModel
511 from rhodecode.model.settings import SettingsModel
512
512
513 sa = meta.Session()
513 sa = meta.Session()
514 repo_model = RepoModel()
514 repo_model = RepoModel()
515 user = User.get_first_super_admin()
515 user = User.get_first_super_admin()
516 added = []
516 added = []
517
517
518 # creation defaults
518 # creation defaults
519 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
519 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
520 enable_statistics = defs.get('repo_enable_statistics')
520 enable_statistics = defs.get('repo_enable_statistics')
521 enable_locking = defs.get('repo_enable_locking')
521 enable_locking = defs.get('repo_enable_locking')
522 enable_downloads = defs.get('repo_enable_downloads')
522 enable_downloads = defs.get('repo_enable_downloads')
523 private = defs.get('repo_private')
523 private = defs.get('repo_private')
524
524
525 for name, repo in initial_repo_list.items():
525 for name, repo in initial_repo_list.items():
526 group = map_groups(name)
526 group = map_groups(name)
527 unicode_name = safe_unicode(name)
527 unicode_name = safe_unicode(name)
528 db_repo = repo_model.get_by_repo_name(unicode_name)
528 db_repo = repo_model.get_by_repo_name(unicode_name)
529 # found repo that is on filesystem not in RhodeCode database
529 # found repo that is on filesystem not in RhodeCode database
530 if not db_repo:
530 if not db_repo:
531 log.info('repository %s not found, creating now', name)
531 log.info('repository %s not found, creating now', name)
532 added.append(name)
532 added.append(name)
533 desc = (repo.description
533 desc = (repo.description
534 if repo.description != 'unknown'
534 if repo.description != 'unknown'
535 else '%s repository' % name)
535 else '%s repository' % name)
536
536
537 db_repo = repo_model._create_repo(
537 db_repo = repo_model._create_repo(
538 repo_name=name,
538 repo_name=name,
539 repo_type=repo.alias,
539 repo_type=repo.alias,
540 description=desc,
540 description=desc,
541 repo_group=getattr(group, 'group_id', None),
541 repo_group=getattr(group, 'group_id', None),
542 owner=user,
542 owner=user,
543 enable_locking=enable_locking,
543 enable_locking=enable_locking,
544 enable_downloads=enable_downloads,
544 enable_downloads=enable_downloads,
545 enable_statistics=enable_statistics,
545 enable_statistics=enable_statistics,
546 private=private,
546 private=private,
547 state=Repository.STATE_CREATED
547 state=Repository.STATE_CREATED
548 )
548 )
549 sa.commit()
549 sa.commit()
550 # we added that repo just now, and make sure we updated server info
550 # we added that repo just now, and make sure we updated server info
551 if db_repo.repo_type == 'git':
551 if db_repo.repo_type == 'git':
552 git_repo = db_repo.scm_instance()
552 git_repo = db_repo.scm_instance()
553 # update repository server-info
553 # update repository server-info
554 log.debug('Running update server info')
554 log.debug('Running update server info')
555 git_repo._update_server_info()
555 git_repo._update_server_info()
556
556
557 db_repo.update_commit_cache()
557 db_repo.update_commit_cache()
558
558
559 config = db_repo._config
559 config = db_repo._config
560 config.set('extensions', 'largefiles', '')
560 config.set('extensions', 'largefiles', '')
561 ScmModel().install_hooks(
561 ScmModel().install_hooks(
562 db_repo.scm_instance(config=config),
562 db_repo.scm_instance(config=config),
563 repo_type=db_repo.repo_type)
563 repo_type=db_repo.repo_type)
564
564
565 removed = []
565 removed = []
566 if remove_obsolete:
566 if remove_obsolete:
567 # remove from database those repositories that are not in the filesystem
567 # remove from database those repositories that are not in the filesystem
568 for repo in sa.query(Repository).all():
568 for repo in sa.query(Repository).all():
569 if repo.repo_name not in initial_repo_list.keys():
569 if repo.repo_name not in initial_repo_list.keys():
570 log.debug("Removing non-existing repository found in db `%s`",
570 log.debug("Removing non-existing repository found in db `%s`",
571 repo.repo_name)
571 repo.repo_name)
572 try:
572 try:
573 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
573 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
574 sa.commit()
574 sa.commit()
575 removed.append(repo.repo_name)
575 removed.append(repo.repo_name)
576 except Exception:
576 except Exception:
577 # don't hold further removals on error
577 # don't hold further removals on error
578 log.error(traceback.format_exc())
578 log.error(traceback.format_exc())
579 sa.rollback()
579 sa.rollback()
580
580
581 def splitter(full_repo_name):
581 def splitter(full_repo_name):
582 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
582 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
583 gr_name = None
583 gr_name = None
584 if len(_parts) == 2:
584 if len(_parts) == 2:
585 gr_name = _parts[0]
585 gr_name = _parts[0]
586 return gr_name
586 return gr_name
587
587
588 initial_repo_group_list = [splitter(x) for x in
588 initial_repo_group_list = [splitter(x) for x in
589 initial_repo_list.keys() if splitter(x)]
589 initial_repo_list.keys() if splitter(x)]
590
590
591 # remove from database those repository groups that are not in the
591 # remove from database those repository groups that are not in the
592 # filesystem due to parent child relationships we need to delete them
592 # filesystem due to parent child relationships we need to delete them
593 # in a specific order of most nested first
593 # in a specific order of most nested first
594 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
594 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
595 nested_sort = lambda gr: len(gr.split('/'))
595 nested_sort = lambda gr: len(gr.split('/'))
596 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
596 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
597 if group_name not in initial_repo_group_list:
597 if group_name not in initial_repo_group_list:
598 repo_group = RepoGroup.get_by_group_name(group_name)
598 repo_group = RepoGroup.get_by_group_name(group_name)
599 if (repo_group.children.all() or
599 if (repo_group.children.all() or
600 not RepoGroupModel().check_exist_filesystem(
600 not RepoGroupModel().check_exist_filesystem(
601 group_name=group_name, exc_on_failure=False)):
601 group_name=group_name, exc_on_failure=False)):
602 continue
602 continue
603
603
604 log.info(
604 log.info(
605 'Removing non-existing repository group found in db `%s`',
605 'Removing non-existing repository group found in db `%s`',
606 group_name)
606 group_name)
607 try:
607 try:
608 RepoGroupModel(sa).delete(group_name, fs_remove=False)
608 RepoGroupModel(sa).delete(group_name, fs_remove=False)
609 sa.commit()
609 sa.commit()
610 removed.append(group_name)
610 removed.append(group_name)
611 except Exception:
611 except Exception:
612 # don't hold further removals on error
612 # don't hold further removals on error
613 log.exception(
613 log.exception(
614 'Unable to remove repository group `%s`',
614 'Unable to remove repository group `%s`',
615 group_name)
615 group_name)
616 sa.rollback()
616 sa.rollback()
617 raise
617 raise
618
618
619 return added, removed
619 return added, removed
620
620
621
621
622 def get_default_cache_settings(settings):
623 cache_settings = {}
624 for key in settings.keys():
625 for prefix in ['beaker.cache.', 'cache.']:
626 if key.startswith(prefix):
627 name = key.split(prefix)[1].strip()
628 cache_settings[name] = settings[key].strip()
629 return cache_settings
630
631
632 # set cache regions for beaker so celery can utilise it
633 def add_cache(settings):
634 from rhodecode.lib import caches
635 cache_settings = {'regions': None}
636 # main cache settings used as default ...
637 cache_settings.update(get_default_cache_settings(settings))
638
639 if cache_settings['regions']:
640 for region in cache_settings['regions'].split(','):
641 region = region.strip()
642 region_settings = {}
643 for key, value in cache_settings.items():
644 if key.startswith(region):
645 region_settings[key.split('.')[1]] = value
646
647 caches.configure_cache_region(
648 region, region_settings, cache_settings)
649
650
651 def load_rcextensions(root_path):
622 def load_rcextensions(root_path):
652 import rhodecode
623 import rhodecode
653 from rhodecode.config import conf
624 from rhodecode.config import conf
654
625
655 path = os.path.join(root_path, 'rcextensions', '__init__.py')
626 path = os.path.join(root_path, 'rcextensions', '__init__.py')
656 if os.path.isfile(path):
627 if os.path.isfile(path):
657 rcext = create_module('rc', path)
628 rcext = create_module('rc', path)
658 EXT = rhodecode.EXTENSIONS = rcext
629 EXT = rhodecode.EXTENSIONS = rcext
659 log.debug('Found rcextensions now loading %s...', rcext)
630 log.debug('Found rcextensions now loading %s...', rcext)
660
631
661 # Additional mappings that are not present in the pygments lexers
632 # Additional mappings that are not present in the pygments lexers
662 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
633 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
663
634
664 # auto check if the module is not missing any data, set to default if is
635 # auto check if the module is not missing any data, set to default if is
665 # this will help autoupdate new feature of rcext module
636 # this will help autoupdate new feature of rcext module
666 #from rhodecode.config import rcextensions
637 #from rhodecode.config import rcextensions
667 #for k in dir(rcextensions):
638 #for k in dir(rcextensions):
668 # if not k.startswith('_') and not hasattr(EXT, k):
639 # if not k.startswith('_') and not hasattr(EXT, k):
669 # setattr(EXT, k, getattr(rcextensions, k))
640 # setattr(EXT, k, getattr(rcextensions, k))
670
641
671
642
672 def get_custom_lexer(extension):
643 def get_custom_lexer(extension):
673 """
644 """
674 returns a custom lexer if it is defined in rcextensions module, or None
645 returns a custom lexer if it is defined in rcextensions module, or None
675 if there's no custom lexer defined
646 if there's no custom lexer defined
676 """
647 """
677 import rhodecode
648 import rhodecode
678 from pygments import lexers
649 from pygments import lexers
679
650
680 # custom override made by RhodeCode
651 # custom override made by RhodeCode
681 if extension in ['mako']:
652 if extension in ['mako']:
682 return lexers.get_lexer_by_name('html+mako')
653 return lexers.get_lexer_by_name('html+mako')
683
654
684 # check if we didn't define this extension as other lexer
655 # check if we didn't define this extension as other lexer
685 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
656 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
686 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
657 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
687 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
658 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
688 return lexers.get_lexer_by_name(_lexer_name)
659 return lexers.get_lexer_by_name(_lexer_name)
689
660
690
661
691 #==============================================================================
662 #==============================================================================
692 # TEST FUNCTIONS AND CREATORS
663 # TEST FUNCTIONS AND CREATORS
693 #==============================================================================
664 #==============================================================================
694 def create_test_index(repo_location, config):
665 def create_test_index(repo_location, config):
695 """
666 """
696 Makes default test index.
667 Makes default test index.
697 """
668 """
698 import rc_testdata
669 import rc_testdata
699
670
700 rc_testdata.extract_search_index(
671 rc_testdata.extract_search_index(
701 'vcs_search_index', os.path.dirname(config['search.location']))
672 'vcs_search_index', os.path.dirname(config['search.location']))
702
673
703
674
704 def create_test_directory(test_path):
675 def create_test_directory(test_path):
705 """
676 """
706 Create test directory if it doesn't exist.
677 Create test directory if it doesn't exist.
707 """
678 """
708 if not os.path.isdir(test_path):
679 if not os.path.isdir(test_path):
709 log.debug('Creating testdir %s', test_path)
680 log.debug('Creating testdir %s', test_path)
710 os.makedirs(test_path)
681 os.makedirs(test_path)
711
682
712
683
713 def create_test_database(test_path, config):
684 def create_test_database(test_path, config):
714 """
685 """
715 Makes a fresh database.
686 Makes a fresh database.
716 """
687 """
717 from rhodecode.lib.db_manage import DbManage
688 from rhodecode.lib.db_manage import DbManage
718
689
719 # PART ONE create db
690 # PART ONE create db
720 dbconf = config['sqlalchemy.db1.url']
691 dbconf = config['sqlalchemy.db1.url']
721 log.debug('making test db %s', dbconf)
692 log.debug('making test db %s', dbconf)
722
693
723 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
694 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
724 tests=True, cli_args={'force_ask': True})
695 tests=True, cli_args={'force_ask': True})
725 dbmanage.create_tables(override=True)
696 dbmanage.create_tables(override=True)
726 dbmanage.set_db_version()
697 dbmanage.set_db_version()
727 # for tests dynamically set new root paths based on generated content
698 # for tests dynamically set new root paths based on generated content
728 dbmanage.create_settings(dbmanage.config_prompt(test_path))
699 dbmanage.create_settings(dbmanage.config_prompt(test_path))
729 dbmanage.create_default_user()
700 dbmanage.create_default_user()
730 dbmanage.create_test_admin_and_users()
701 dbmanage.create_test_admin_and_users()
731 dbmanage.create_permissions()
702 dbmanage.create_permissions()
732 dbmanage.populate_default_permissions()
703 dbmanage.populate_default_permissions()
733 Session().commit()
704 Session().commit()
734
705
735
706
736 def create_test_repositories(test_path, config):
707 def create_test_repositories(test_path, config):
737 """
708 """
738 Creates test repositories in the temporary directory. Repositories are
709 Creates test repositories in the temporary directory. Repositories are
739 extracted from archives within the rc_testdata package.
710 extracted from archives within the rc_testdata package.
740 """
711 """
741 import rc_testdata
712 import rc_testdata
742 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
713 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
743
714
744 log.debug('making test vcs repositories')
715 log.debug('making test vcs repositories')
745
716
746 idx_path = config['search.location']
717 idx_path = config['search.location']
747 data_path = config['cache_dir']
718 data_path = config['cache_dir']
748
719
749 # clean index and data
720 # clean index and data
750 if idx_path and os.path.exists(idx_path):
721 if idx_path and os.path.exists(idx_path):
751 log.debug('remove %s', idx_path)
722 log.debug('remove %s', idx_path)
752 shutil.rmtree(idx_path)
723 shutil.rmtree(idx_path)
753
724
754 if data_path and os.path.exists(data_path):
725 if data_path and os.path.exists(data_path):
755 log.debug('remove %s', data_path)
726 log.debug('remove %s', data_path)
756 shutil.rmtree(data_path)
727 shutil.rmtree(data_path)
757
728
758 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
729 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
759 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
730 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
760
731
761 # Note: Subversion is in the process of being integrated with the system,
732 # Note: Subversion is in the process of being integrated with the system,
762 # until we have a properly packed version of the test svn repository, this
733 # until we have a properly packed version of the test svn repository, this
763 # tries to copy over the repo from a package "rc_testdata"
734 # tries to copy over the repo from a package "rc_testdata"
764 svn_repo_path = rc_testdata.get_svn_repo_archive()
735 svn_repo_path = rc_testdata.get_svn_repo_archive()
765 with tarfile.open(svn_repo_path) as tar:
736 with tarfile.open(svn_repo_path) as tar:
766 tar.extractall(jn(test_path, SVN_REPO))
737 tar.extractall(jn(test_path, SVN_REPO))
767
738
768
739
769 def password_changed(auth_user, session):
740 def password_changed(auth_user, session):
770 # Never report password change in case of default user or anonymous user.
741 # Never report password change in case of default user or anonymous user.
771 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
742 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
772 return False
743 return False
773
744
774 password_hash = md5(auth_user.password) if auth_user.password else None
745 password_hash = md5(auth_user.password) if auth_user.password else None
775 rhodecode_user = session.get('rhodecode_user', {})
746 rhodecode_user = session.get('rhodecode_user', {})
776 session_password_hash = rhodecode_user.get('password', '')
747 session_password_hash = rhodecode_user.get('password', '')
777 return password_hash != session_password_hash
748 return password_hash != session_password_hash
778
749
779
750
780 def read_opensource_licenses():
751 def read_opensource_licenses():
781 global _license_cache
752 global _license_cache
782
753
783 if not _license_cache:
754 if not _license_cache:
784 licenses = pkg_resources.resource_string(
755 licenses = pkg_resources.resource_string(
785 'rhodecode', 'config/licenses.json')
756 'rhodecode', 'config/licenses.json')
786 _license_cache = json.loads(licenses)
757 _license_cache = json.loads(licenses)
787
758
788 return _license_cache
759 return _license_cache
789
760
790
761
791 def generate_platform_uuid():
762 def generate_platform_uuid():
792 """
763 """
793 Generates platform UUID based on it's name
764 Generates platform UUID based on it's name
794 """
765 """
795 import platform
766 import platform
796
767
797 try:
768 try:
798 uuid_list = [platform.platform()]
769 uuid_list = [platform.platform()]
799 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
770 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
800 except Exception as e:
771 except Exception as e:
801 log.error('Failed to generate host uuid: %s' % e)
772 log.error('Failed to generate host uuid: %s' % e)
802 return 'UNDEFINED'
773 return 'UNDEFINED'
@@ -1,768 +1,768 b''
1
1
2
2
3 ################################################################################
3 ################################################################################
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10
10
11 ################################################################################
11 ################################################################################
12 ## EMAIL CONFIGURATION ##
12 ## EMAIL CONFIGURATION ##
13 ## Uncomment and replace with the email address which should receive ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
16 ################################################################################
17
17
18 ## prefix all emails subjects with given prefix, helps filtering out emails
18 ## prefix all emails subjects with given prefix, helps filtering out emails
19 #email_prefix = [RhodeCode]
19 #email_prefix = [RhodeCode]
20
20
21 ## email FROM address all mails will be sent
21 ## email FROM address all mails will be sent
22 #app_email_from = rhodecode-noreply@localhost
22 #app_email_from = rhodecode-noreply@localhost
23
23
24 ## Uncomment and replace with the address which should receive any error report
24 ## Uncomment and replace with the address which should receive any error report
25 ## note: using appenlight for error handling doesn't need this to be uncommented
25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 #email_to = admin@localhost
26 #email_to = admin@localhost
27
27
28 ## in case of Application errors, sent an error email form
28 ## in case of Application errors, sent an error email form
29 #error_email_from = rhodecode_error@localhost
29 #error_email_from = rhodecode_error@localhost
30
30
31 ## additional error message to be send in case of server crash
31 ## additional error message to be send in case of server crash
32 #error_message =
32 #error_message =
33
33
34
34
35 #smtp_server = mail.server.com
35 #smtp_server = mail.server.com
36 #smtp_username =
36 #smtp_username =
37 #smtp_password =
37 #smtp_password =
38 #smtp_port =
38 #smtp_port =
39 #smtp_use_tls = false
39 #smtp_use_tls = false
40 #smtp_use_ssl = true
40 #smtp_use_ssl = true
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
42 #smtp_auth =
42 #smtp_auth =
43
43
44 [server:main]
44 [server:main]
45 ## COMMON ##
45 ## COMMON ##
46 host = 0.0.0.0
46 host = 0.0.0.0
47 port = 5000
47 port = 5000
48
48
49 ##################################
49 ##################################
50 ## WAITRESS WSGI SERVER ##
50 ## WAITRESS WSGI SERVER ##
51 ## Recommended for Development ##
51 ## Recommended for Development ##
52 ##################################
52 ##################################
53
53
54 use = egg:waitress#main
54 use = egg:waitress#main
55 ## number of worker threads
55 ## number of worker threads
56 threads = 5
56 threads = 5
57 ## MAX BODY SIZE 100GB
57 ## MAX BODY SIZE 100GB
58 max_request_body_size = 107374182400
58 max_request_body_size = 107374182400
59 ## Use poll instead of select, fixes file descriptors limits problems.
59 ## Use poll instead of select, fixes file descriptors limits problems.
60 ## May not work on old windows systems.
60 ## May not work on old windows systems.
61 asyncore_use_poll = true
61 asyncore_use_poll = true
62
62
63
63
64 ##########################
64 ##########################
65 ## GUNICORN WSGI SERVER ##
65 ## GUNICORN WSGI SERVER ##
66 ##########################
66 ##########################
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
68
68
69 #use = egg:gunicorn#main
69 #use = egg:gunicorn#main
70 ## Sets the number of process workers. You must set `instance_id = *`
70 ## Sets the number of process workers. You must set `instance_id = *`
71 ## when this option is set to more than one worker, recommended
71 ## when this option is set to more than one worker, recommended
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
73 ## The `instance_id = *` must be set in the [app:main] section below
73 ## The `instance_id = *` must be set in the [app:main] section below
74 #workers = 2
74 #workers = 2
75 ## number of threads for each of the worker, must be set to 1 for gevent
75 ## number of threads for each of the worker, must be set to 1 for gevent
76 ## generally recommened to be at 1
76 ## generally recommened to be at 1
77 #threads = 1
77 #threads = 1
78 ## process name
78 ## process name
79 #proc_name = rhodecode
79 #proc_name = rhodecode
80 ## type of worker class, one of sync, gevent
80 ## type of worker class, one of sync, gevent
81 ## recommended for bigger setup is using of of other than sync one
81 ## recommended for bigger setup is using of of other than sync one
82 #worker_class = sync
82 #worker_class = sync
83 ## The maximum number of simultaneous clients. Valid only for Gevent
83 ## The maximum number of simultaneous clients. Valid only for Gevent
84 #worker_connections = 10
84 #worker_connections = 10
85 ## max number of requests that worker will handle before being gracefully
85 ## max number of requests that worker will handle before being gracefully
86 ## restarted, could prevent memory leaks
86 ## restarted, could prevent memory leaks
87 #max_requests = 1000
87 #max_requests = 1000
88 #max_requests_jitter = 30
88 #max_requests_jitter = 30
89 ## amount of time a worker can spend with handling a request before it
89 ## amount of time a worker can spend with handling a request before it
90 ## gets killed and restarted. Set to 6hrs
90 ## gets killed and restarted. Set to 6hrs
91 #timeout = 21600
91 #timeout = 21600
92
92
93 ## UWSGI ##
93 ## UWSGI ##
94 ## run with uwsgi --ini-paste-logged <inifile.ini>
94 ## run with uwsgi --ini-paste-logged <inifile.ini>
95 #[uwsgi]
95 #[uwsgi]
96 #socket = /tmp/uwsgi.sock
96 #socket = /tmp/uwsgi.sock
97 #master = true
97 #master = true
98 #http = 127.0.0.1:5000
98 #http = 127.0.0.1:5000
99
99
100 ## set as deamon and redirect all output to file
100 ## set as deamon and redirect all output to file
101 #daemonize = ./uwsgi_rhodecode.log
101 #daemonize = ./uwsgi_rhodecode.log
102
102
103 ## master process PID
103 ## master process PID
104 #pidfile = ./uwsgi_rhodecode.pid
104 #pidfile = ./uwsgi_rhodecode.pid
105
105
106 ## stats server with workers statistics, use uwsgitop
106 ## stats server with workers statistics, use uwsgitop
107 ## for monitoring, `uwsgitop 127.0.0.1:1717`
107 ## for monitoring, `uwsgitop 127.0.0.1:1717`
108 #stats = 127.0.0.1:1717
108 #stats = 127.0.0.1:1717
109 #memory-report = true
109 #memory-report = true
110
110
111 ## log 5XX errors
111 ## log 5XX errors
112 #log-5xx = true
112 #log-5xx = true
113
113
114 ## Set the socket listen queue size.
114 ## Set the socket listen queue size.
115 #listen = 256
115 #listen = 256
116
116
117 ## Gracefully Reload workers after the specified amount of managed requests
117 ## Gracefully Reload workers after the specified amount of managed requests
118 ## (avoid memory leaks).
118 ## (avoid memory leaks).
119 #max-requests = 1000
119 #max-requests = 1000
120
120
121 ## enable large buffers
121 ## enable large buffers
122 #buffer-size=65535
122 #buffer-size=65535
123
123
124 ## socket and http timeouts ##
124 ## socket and http timeouts ##
125 #http-timeout=3600
125 #http-timeout=3600
126 #socket-timeout=3600
126 #socket-timeout=3600
127
127
128 ## Log requests slower than the specified number of milliseconds.
128 ## Log requests slower than the specified number of milliseconds.
129 #log-slow = 10
129 #log-slow = 10
130
130
131 ## Exit if no app can be loaded.
131 ## Exit if no app can be loaded.
132 #need-app = true
132 #need-app = true
133
133
134 ## Set lazy mode (load apps in workers instead of master).
134 ## Set lazy mode (load apps in workers instead of master).
135 #lazy = true
135 #lazy = true
136
136
137 ## scaling ##
137 ## scaling ##
138 ## set cheaper algorithm to use, if not set default will be used
138 ## set cheaper algorithm to use, if not set default will be used
139 #cheaper-algo = spare
139 #cheaper-algo = spare
140
140
141 ## minimum number of workers to keep at all times
141 ## minimum number of workers to keep at all times
142 #cheaper = 1
142 #cheaper = 1
143
143
144 ## number of workers to spawn at startup
144 ## number of workers to spawn at startup
145 #cheaper-initial = 1
145 #cheaper-initial = 1
146
146
147 ## maximum number of workers that can be spawned
147 ## maximum number of workers that can be spawned
148 #workers = 4
148 #workers = 4
149
149
150 ## how many workers should be spawned at a time
150 ## how many workers should be spawned at a time
151 #cheaper-step = 1
151 #cheaper-step = 1
152
152
153 ## prefix middleware for RhodeCode.
153 ## prefix middleware for RhodeCode.
154 ## recommended when using proxy setup.
154 ## recommended when using proxy setup.
155 ## allows to set RhodeCode under a prefix in server.
155 ## allows to set RhodeCode under a prefix in server.
156 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
156 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
157 ## And set your prefix like: `prefix = /custom_prefix`
157 ## And set your prefix like: `prefix = /custom_prefix`
158 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
158 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
159 ## to make your cookies only work on prefix url
159 ## to make your cookies only work on prefix url
160 [filter:proxy-prefix]
160 [filter:proxy-prefix]
161 use = egg:PasteDeploy#prefix
161 use = egg:PasteDeploy#prefix
162 prefix = /
162 prefix = /
163
163
164 [app:main]
164 [app:main]
165 is_test = True
165 is_test = True
166 use = egg:rhodecode-enterprise-ce
166 use = egg:rhodecode-enterprise-ce
167
167
168 ## enable proxy prefix middleware, defined above
168 ## enable proxy prefix middleware, defined above
169 #filter-with = proxy-prefix
169 #filter-with = proxy-prefix
170
170
171
171
172 ## RHODECODE PLUGINS ##
172 ## RHODECODE PLUGINS ##
173 rhodecode.includes = rhodecode.api
173 rhodecode.includes = rhodecode.api
174
174
175 # api prefix url
175 # api prefix url
176 rhodecode.api.url = /_admin/api
176 rhodecode.api.url = /_admin/api
177
177
178
178
179 ## END RHODECODE PLUGINS ##
179 ## END RHODECODE PLUGINS ##
180
180
181 ## encryption key used to encrypt social plugin tokens,
181 ## encryption key used to encrypt social plugin tokens,
182 ## remote_urls with credentials etc, if not set it defaults to
182 ## remote_urls with credentials etc, if not set it defaults to
183 ## `beaker.session.secret`
183 ## `beaker.session.secret`
184 #rhodecode.encrypted_values.secret =
184 #rhodecode.encrypted_values.secret =
185
185
186 ## decryption strict mode (enabled by default). It controls if decryption raises
186 ## decryption strict mode (enabled by default). It controls if decryption raises
187 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
187 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
188 #rhodecode.encrypted_values.strict = false
188 #rhodecode.encrypted_values.strict = false
189
189
190 ## return gzipped responses from Rhodecode (static files/application)
190 ## return gzipped responses from Rhodecode (static files/application)
191 gzip_responses = false
191 gzip_responses = false
192
192
193 ## autogenerate javascript routes file on startup
193 ## autogenerate javascript routes file on startup
194 generate_js_files = false
194 generate_js_files = false
195
195
196 ## Optional Languages
196 ## Optional Languages
197 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
197 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
198 lang = en
198 lang = en
199
199
200 ## perform a full repository scan on each server start, this should be
200 ## perform a full repository scan on each server start, this should be
201 ## set to false after first startup, to allow faster server restarts.
201 ## set to false after first startup, to allow faster server restarts.
202 startup.import_repos = true
202 startup.import_repos = true
203
203
204 ## Uncomment and set this path to use archive download cache.
204 ## Uncomment and set this path to use archive download cache.
205 ## Once enabled, generated archives will be cached at this location
205 ## Once enabled, generated archives will be cached at this location
206 ## and served from the cache during subsequent requests for the same archive of
206 ## and served from the cache during subsequent requests for the same archive of
207 ## the repository.
207 ## the repository.
208 #archive_cache_dir = /tmp/tarballcache
208 #archive_cache_dir = /tmp/tarballcache
209
209
210 ## URL at which the application is running. This is used for bootstraping
210 ## URL at which the application is running. This is used for bootstraping
211 ## requests in context when no web request is available. Used in ishell, or
211 ## requests in context when no web request is available. Used in ishell, or
212 ## SSH calls. Set this for events to receive proper url for SSH calls.
212 ## SSH calls. Set this for events to receive proper url for SSH calls.
213 app.base_url = http://rhodecode.local
213 app.base_url = http://rhodecode.local
214
214
215 ## change this to unique ID for security
215 ## change this to unique ID for security
216 app_instance_uuid = rc-production
216 app_instance_uuid = rc-production
217
217
218 ## cut off limit for large diffs (size in bytes)
218 ## cut off limit for large diffs (size in bytes)
219 cut_off_limit_diff = 1024000
219 cut_off_limit_diff = 1024000
220 cut_off_limit_file = 256000
220 cut_off_limit_file = 256000
221
221
222 ## use cache version of scm repo everywhere
222 ## use cache version of scm repo everywhere
223 vcs_full_cache = false
223 vcs_full_cache = false
224
224
225 ## force https in RhodeCode, fixes https redirects, assumes it's always https
225 ## force https in RhodeCode, fixes https redirects, assumes it's always https
226 ## Normally this is controlled by proper http flags sent from http server
226 ## Normally this is controlled by proper http flags sent from http server
227 force_https = false
227 force_https = false
228
228
229 ## use Strict-Transport-Security headers
229 ## use Strict-Transport-Security headers
230 use_htsts = false
230 use_htsts = false
231
231
232 ## number of commits stats will parse on each iteration
232 ## number of commits stats will parse on each iteration
233 commit_parse_limit = 25
233 commit_parse_limit = 25
234
234
235 ## git rev filter option, --all is the default filter, if you need to
235 ## git rev filter option, --all is the default filter, if you need to
236 ## hide all refs in changelog switch this to --branches --tags
236 ## hide all refs in changelog switch this to --branches --tags
237 git_rev_filter = --all
237 git_rev_filter = --all
238
238
239 # Set to true if your repos are exposed using the dumb protocol
239 # Set to true if your repos are exposed using the dumb protocol
240 git_update_server_info = false
240 git_update_server_info = false
241
241
242 ## RSS/ATOM feed options
242 ## RSS/ATOM feed options
243 rss_cut_off_limit = 256000
243 rss_cut_off_limit = 256000
244 rss_items_per_page = 10
244 rss_items_per_page = 10
245 rss_include_diff = false
245 rss_include_diff = false
246
246
247 ## gist URL alias, used to create nicer urls for gist. This should be an
247 ## gist URL alias, used to create nicer urls for gist. This should be an
248 ## url that does rewrites to _admin/gists/{gistid}.
248 ## url that does rewrites to _admin/gists/{gistid}.
249 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
249 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
250 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
250 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
251 gist_alias_url =
251 gist_alias_url =
252
252
253 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
253 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
254 ## used for access.
254 ## used for access.
255 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
255 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
256 ## came from the the logged in user who own this authentication token.
256 ## came from the the logged in user who own this authentication token.
257 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
257 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
258 ## authentication token. Such view would be only accessible when used together
258 ## authentication token. Such view would be only accessible when used together
259 ## with this authentication token
259 ## with this authentication token
260 ##
260 ##
261 ## list of all views can be found under `/_admin/permissions/auth_token_access`
261 ## list of all views can be found under `/_admin/permissions/auth_token_access`
262 ## The list should be "," separated and on a single line.
262 ## The list should be "," separated and on a single line.
263 ##
263 ##
264 ## Most common views to enable:
264 ## Most common views to enable:
265 # RepoCommitsView:repo_commit_download
265 # RepoCommitsView:repo_commit_download
266 # RepoCommitsView:repo_commit_patch
266 # RepoCommitsView:repo_commit_patch
267 # RepoCommitsView:repo_commit_raw
267 # RepoCommitsView:repo_commit_raw
268 # RepoCommitsView:repo_commit_raw@TOKEN
268 # RepoCommitsView:repo_commit_raw@TOKEN
269 # RepoFilesView:repo_files_diff
269 # RepoFilesView:repo_files_diff
270 # RepoFilesView:repo_archivefile
270 # RepoFilesView:repo_archivefile
271 # RepoFilesView:repo_file_raw
271 # RepoFilesView:repo_file_raw
272 # GistView:*
272 # GistView:*
273 api_access_controllers_whitelist =
273 api_access_controllers_whitelist =
274
274
275 ## default encoding used to convert from and to unicode
275 ## default encoding used to convert from and to unicode
276 ## can be also a comma separated list of encoding in case of mixed encodings
276 ## can be also a comma separated list of encoding in case of mixed encodings
277 default_encoding = UTF-8
277 default_encoding = UTF-8
278
278
279 ## instance-id prefix
279 ## instance-id prefix
280 ## a prefix key for this instance used for cache invalidation when running
280 ## a prefix key for this instance used for cache invalidation when running
281 ## multiple instances of rhodecode, make sure it's globally unique for
281 ## multiple instances of rhodecode, make sure it's globally unique for
282 ## all running rhodecode instances. Leave empty if you don't use it
282 ## all running rhodecode instances. Leave empty if you don't use it
283 instance_id =
283 instance_id =
284
284
285 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
285 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
286 ## of an authentication plugin also if it is disabled by it's settings.
286 ## of an authentication plugin also if it is disabled by it's settings.
287 ## This could be useful if you are unable to log in to the system due to broken
287 ## This could be useful if you are unable to log in to the system due to broken
288 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
288 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
289 ## module to log in again and fix the settings.
289 ## module to log in again and fix the settings.
290 ##
290 ##
291 ## Available builtin plugin IDs (hash is part of the ID):
291 ## Available builtin plugin IDs (hash is part of the ID):
292 ## egg:rhodecode-enterprise-ce#rhodecode
292 ## egg:rhodecode-enterprise-ce#rhodecode
293 ## egg:rhodecode-enterprise-ce#pam
293 ## egg:rhodecode-enterprise-ce#pam
294 ## egg:rhodecode-enterprise-ce#ldap
294 ## egg:rhodecode-enterprise-ce#ldap
295 ## egg:rhodecode-enterprise-ce#jasig_cas
295 ## egg:rhodecode-enterprise-ce#jasig_cas
296 ## egg:rhodecode-enterprise-ce#headers
296 ## egg:rhodecode-enterprise-ce#headers
297 ## egg:rhodecode-enterprise-ce#crowd
297 ## egg:rhodecode-enterprise-ce#crowd
298 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
298 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
299
299
300 ## alternative return HTTP header for failed authentication. Default HTTP
300 ## alternative return HTTP header for failed authentication. Default HTTP
301 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
301 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
302 ## handling that causing a series of failed authentication calls.
302 ## handling that causing a series of failed authentication calls.
303 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
303 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
304 ## This will be served instead of default 401 on bad authnetication
304 ## This will be served instead of default 401 on bad authnetication
305 auth_ret_code =
305 auth_ret_code =
306
306
307 ## use special detection method when serving auth_ret_code, instead of serving
307 ## use special detection method when serving auth_ret_code, instead of serving
308 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
308 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
309 ## and then serve auth_ret_code to clients
309 ## and then serve auth_ret_code to clients
310 auth_ret_code_detection = false
310 auth_ret_code_detection = false
311
311
312 ## locking return code. When repository is locked return this HTTP code. 2XX
312 ## locking return code. When repository is locked return this HTTP code. 2XX
313 ## codes don't break the transactions while 4XX codes do
313 ## codes don't break the transactions while 4XX codes do
314 lock_ret_code = 423
314 lock_ret_code = 423
315
315
316 ## allows to change the repository location in settings page
316 ## allows to change the repository location in settings page
317 allow_repo_location_change = true
317 allow_repo_location_change = true
318
318
319 ## allows to setup custom hooks in settings page
319 ## allows to setup custom hooks in settings page
320 allow_custom_hooks_settings = true
320 allow_custom_hooks_settings = true
321
321
322 ## generated license token, goto license page in RhodeCode settings to obtain
322 ## generated license token, goto license page in RhodeCode settings to obtain
323 ## new token
323 ## new token
324 license_token = abra-cada-bra1-rce3
324 license_token = abra-cada-bra1-rce3
325
325
326 ## supervisor connection uri, for managing supervisor and logs.
326 ## supervisor connection uri, for managing supervisor and logs.
327 supervisor.uri =
327 supervisor.uri =
328 ## supervisord group name/id we only want this RC instance to handle
328 ## supervisord group name/id we only want this RC instance to handle
329 supervisor.group_id = dev
329 supervisor.group_id = dev
330
330
331 ## Display extended labs settings
331 ## Display extended labs settings
332 labs_settings_active = true
332 labs_settings_active = true
333
333
334 ####################################
334 ####################################
335 ### CELERY CONFIG ####
335 ### CELERY CONFIG ####
336 ####################################
336 ####################################
337 use_celery = false
337 use_celery = false
338 broker.host = localhost
338 broker.host = localhost
339 broker.vhost = rabbitmqhost
339 broker.vhost = rabbitmqhost
340 broker.port = 5672
340 broker.port = 5672
341 broker.user = rabbitmq
341 broker.user = rabbitmq
342 broker.password = qweqwe
342 broker.password = qweqwe
343
343
344 celery.imports = rhodecode.lib.celerylib.tasks
344 celery.imports = rhodecode.lib.celerylib.tasks
345
345
346 celery.result.backend = amqp
346 celery.result.backend = amqp
347 celery.result.dburi = amqp://
347 celery.result.dburi = amqp://
348 celery.result.serialier = json
348 celery.result.serialier = json
349
349
350 #celery.send.task.error.emails = true
350 #celery.send.task.error.emails = true
351 #celery.amqp.task.result.expires = 18000
351 #celery.amqp.task.result.expires = 18000
352
352
353 celeryd.concurrency = 2
353 celeryd.concurrency = 2
354 #celeryd.log.file = celeryd.log
354 #celeryd.log.file = celeryd.log
355 celeryd.log.level = debug
355 celeryd.log.level = debug
356 celeryd.max.tasks.per.child = 1
356 celeryd.max.tasks.per.child = 1
357
357
358 ## tasks will never be sent to the queue, but executed locally instead.
358 ## tasks will never be sent to the queue, but executed locally instead.
359 celery.always.eager = false
359 celery.always.eager = false
360
360
361 ####################################
361 ####################################
362 ### BEAKER CACHE ####
362 ### BEAKER CACHE ####
363 ####################################
363 ####################################
364 # default cache dir for templates. Putting this into a ramdisk
364 # default cache dir for templates. Putting this into a ramdisk
365 ## can boost performance, eg. %(here)s/data_ramdisk
365 ## can boost performance, eg. %(here)s/data_ramdisk
366 cache_dir = %(here)s/data
366 cache_dir = %(here)s/data
367
367
368 ## locking and default file storage for Beaker. Putting this into a ramdisk
368 ## locking and default file storage for Beaker. Putting this into a ramdisk
369 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
369 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
370 beaker.cache.data_dir = %(here)s/rc/data/cache/beaker_data
370 beaker.cache.data_dir = %(here)s/rc/data/cache/beaker_data
371 beaker.cache.lock_dir = %(here)s/rc/data/cache/beaker_lock
371 beaker.cache.lock_dir = %(here)s/rc/data/cache/beaker_lock
372
372
373 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
373 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
374
374
375 beaker.cache.super_short_term.type = memory
375 beaker.cache.super_short_term.type = memory
376 beaker.cache.super_short_term.expire = 1
376 beaker.cache.super_short_term.expire = 1
377 beaker.cache.super_short_term.key_length = 256
377 beaker.cache.super_short_term.key_length = 256
378
378
379 beaker.cache.short_term.type = memory
379 beaker.cache.short_term.type = memory
380 beaker.cache.short_term.expire = 60
380 beaker.cache.short_term.expire = 60
381 beaker.cache.short_term.key_length = 256
381 beaker.cache.short_term.key_length = 256
382
382
383 beaker.cache.long_term.type = memory
383 beaker.cache.long_term.type = memory
384 beaker.cache.long_term.expire = 36000
384 beaker.cache.long_term.expire = 36000
385 beaker.cache.long_term.key_length = 256
385 beaker.cache.long_term.key_length = 256
386
386
387 beaker.cache.sql_cache_short.type = memory
387 beaker.cache.sql_cache_short.type = memory
388 beaker.cache.sql_cache_short.expire = 1
388 beaker.cache.sql_cache_short.expire = 1
389 beaker.cache.sql_cache_short.key_length = 256
389 beaker.cache.sql_cache_short.key_length = 256
390
390
391 ## default is memory cache, configure only if required
391 ## default is memory cache, configure only if required
392 ## using multi-node or multi-worker setup
392 ## using multi-node or multi-worker setup
393 beaker.cache.auth_plugins.type = memory
393 #beaker.cache.auth_plugins.type = memory
394 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
394 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
395 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
395 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
396 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
396 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
397 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
397 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
398 #beaker.cache.auth_plugins.sa.pool_size = 10
398 #beaker.cache.auth_plugins.sa.pool_size = 10
399 #beaker.cache.auth_plugins.sa.max_overflow = 0
399 #beaker.cache.auth_plugins.sa.max_overflow = 0
400
400
401 beaker.cache.repo_cache_long.type = memorylru_base
401 beaker.cache.repo_cache_long.type = memorylru_base
402 beaker.cache.repo_cache_long.max_items = 4096
402 beaker.cache.repo_cache_long.max_items = 4096
403 beaker.cache.repo_cache_long.expire = 2592000
403 beaker.cache.repo_cache_long.expire = 2592000
404
404
405 ## default is memorylru_base cache, configure only if required
405 ## default is memorylru_base cache, configure only if required
406 ## using multi-node or multi-worker setup
406 ## using multi-node or multi-worker setup
407 #beaker.cache.repo_cache_long.type = ext:memcached
407 #beaker.cache.repo_cache_long.type = ext:memcached
408 #beaker.cache.repo_cache_long.url = localhost:11211
408 #beaker.cache.repo_cache_long.url = localhost:11211
409 #beaker.cache.repo_cache_long.expire = 1209600
409 #beaker.cache.repo_cache_long.expire = 1209600
410 #beaker.cache.repo_cache_long.key_length = 256
410 #beaker.cache.repo_cache_long.key_length = 256
411
411
412 ####################################
412 ####################################
413 ### BEAKER SESSION ####
413 ### BEAKER SESSION ####
414 ####################################
414 ####################################
415
415
416 ## .session.type is type of storage options for the session, current allowed
416 ## .session.type is type of storage options for the session, current allowed
417 ## types are file, ext:memcached, ext:database, and memory (default).
417 ## types are file, ext:memcached, ext:database, and memory (default).
418 beaker.session.type = file
418 beaker.session.type = file
419 beaker.session.data_dir = %(here)s/rc/data/sessions/data
419 beaker.session.data_dir = %(here)s/rc/data/sessions/data
420
420
421 ## db based session, fast, and allows easy management over logged in users
421 ## db based session, fast, and allows easy management over logged in users
422 #beaker.session.type = ext:database
422 #beaker.session.type = ext:database
423 #beaker.session.table_name = db_session
423 #beaker.session.table_name = db_session
424 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
424 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
425 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
425 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
426 #beaker.session.sa.pool_recycle = 3600
426 #beaker.session.sa.pool_recycle = 3600
427 #beaker.session.sa.echo = false
427 #beaker.session.sa.echo = false
428
428
429 beaker.session.key = rhodecode
429 beaker.session.key = rhodecode
430 beaker.session.secret = test-rc-uytcxaz
430 beaker.session.secret = test-rc-uytcxaz
431 beaker.session.lock_dir = %(here)s/rc/data/sessions/lock
431 beaker.session.lock_dir = %(here)s/rc/data/sessions/lock
432
432
433 ## Secure encrypted cookie. Requires AES and AES python libraries
433 ## Secure encrypted cookie. Requires AES and AES python libraries
434 ## you must disable beaker.session.secret to use this
434 ## you must disable beaker.session.secret to use this
435 #beaker.session.encrypt_key = key_for_encryption
435 #beaker.session.encrypt_key = key_for_encryption
436 #beaker.session.validate_key = validation_key
436 #beaker.session.validate_key = validation_key
437
437
438 ## sets session as invalid(also logging out user) if it haven not been
438 ## sets session as invalid(also logging out user) if it haven not been
439 ## accessed for given amount of time in seconds
439 ## accessed for given amount of time in seconds
440 beaker.session.timeout = 2592000
440 beaker.session.timeout = 2592000
441 beaker.session.httponly = true
441 beaker.session.httponly = true
442 ## Path to use for the cookie. Set to prefix if you use prefix middleware
442 ## Path to use for the cookie. Set to prefix if you use prefix middleware
443 #beaker.session.cookie_path = /custom_prefix
443 #beaker.session.cookie_path = /custom_prefix
444
444
445 ## uncomment for https secure cookie
445 ## uncomment for https secure cookie
446 beaker.session.secure = false
446 beaker.session.secure = false
447
447
448 ## auto save the session to not to use .save()
448 ## auto save the session to not to use .save()
449 beaker.session.auto = false
449 beaker.session.auto = false
450
450
451 ## default cookie expiration time in seconds, set to `true` to set expire
451 ## default cookie expiration time in seconds, set to `true` to set expire
452 ## at browser close
452 ## at browser close
453 #beaker.session.cookie_expires = 3600
453 #beaker.session.cookie_expires = 3600
454
454
455 ###################################
455 ###################################
456 ## SEARCH INDEXING CONFIGURATION ##
456 ## SEARCH INDEXING CONFIGURATION ##
457 ###################################
457 ###################################
458 ## Full text search indexer is available in rhodecode-tools under
458 ## Full text search indexer is available in rhodecode-tools under
459 ## `rhodecode-tools index` command
459 ## `rhodecode-tools index` command
460
460
461 ## WHOOSH Backend, doesn't require additional services to run
461 ## WHOOSH Backend, doesn't require additional services to run
462 ## it works good with few dozen repos
462 ## it works good with few dozen repos
463 search.module = rhodecode.lib.index.whoosh
463 search.module = rhodecode.lib.index.whoosh
464 search.location = %(here)s/data/index
464 search.location = %(here)s/data/index
465
465
466 ########################################
466 ########################################
467 ### CHANNELSTREAM CONFIG ####
467 ### CHANNELSTREAM CONFIG ####
468 ########################################
468 ########################################
469 ## channelstream enables persistent connections and live notification
469 ## channelstream enables persistent connections and live notification
470 ## in the system. It's also used by the chat system
470 ## in the system. It's also used by the chat system
471
471
472 channelstream.enabled = false
472 channelstream.enabled = false
473
473
474 ## server address for channelstream server on the backend
474 ## server address for channelstream server on the backend
475 channelstream.server = 127.0.0.1:9800
475 channelstream.server = 127.0.0.1:9800
476 ## location of the channelstream server from outside world
476 ## location of the channelstream server from outside world
477 ## use ws:// for http or wss:// for https. This address needs to be handled
477 ## use ws:// for http or wss:// for https. This address needs to be handled
478 ## by external HTTP server such as Nginx or Apache
478 ## by external HTTP server such as Nginx or Apache
479 ## see nginx/apache configuration examples in our docs
479 ## see nginx/apache configuration examples in our docs
480 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
480 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
481 channelstream.secret = secret
481 channelstream.secret = secret
482 channelstream.history.location = %(here)s/channelstream_history
482 channelstream.history.location = %(here)s/channelstream_history
483
483
484 ## Internal application path that Javascript uses to connect into.
484 ## Internal application path that Javascript uses to connect into.
485 ## If you use proxy-prefix the prefix should be added before /_channelstream
485 ## If you use proxy-prefix the prefix should be added before /_channelstream
486 channelstream.proxy_path = /_channelstream
486 channelstream.proxy_path = /_channelstream
487
487
488
488
489 ###################################
489 ###################################
490 ## APPENLIGHT CONFIG ##
490 ## APPENLIGHT CONFIG ##
491 ###################################
491 ###################################
492
492
493 ## Appenlight is tailored to work with RhodeCode, see
493 ## Appenlight is tailored to work with RhodeCode, see
494 ## http://appenlight.com for details how to obtain an account
494 ## http://appenlight.com for details how to obtain an account
495
495
496 ## appenlight integration enabled
496 ## appenlight integration enabled
497 appenlight = false
497 appenlight = false
498
498
499 appenlight.server_url = https://api.appenlight.com
499 appenlight.server_url = https://api.appenlight.com
500 appenlight.api_key = YOUR_API_KEY
500 appenlight.api_key = YOUR_API_KEY
501 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
501 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
502
502
503 # used for JS client
503 # used for JS client
504 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
504 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
505
505
506 ## TWEAK AMOUNT OF INFO SENT HERE
506 ## TWEAK AMOUNT OF INFO SENT HERE
507
507
508 ## enables 404 error logging (default False)
508 ## enables 404 error logging (default False)
509 appenlight.report_404 = false
509 appenlight.report_404 = false
510
510
511 ## time in seconds after request is considered being slow (default 1)
511 ## time in seconds after request is considered being slow (default 1)
512 appenlight.slow_request_time = 1
512 appenlight.slow_request_time = 1
513
513
514 ## record slow requests in application
514 ## record slow requests in application
515 ## (needs to be enabled for slow datastore recording and time tracking)
515 ## (needs to be enabled for slow datastore recording and time tracking)
516 appenlight.slow_requests = true
516 appenlight.slow_requests = true
517
517
518 ## enable hooking to application loggers
518 ## enable hooking to application loggers
519 appenlight.logging = true
519 appenlight.logging = true
520
520
521 ## minimum log level for log capture
521 ## minimum log level for log capture
522 appenlight.logging.level = WARNING
522 appenlight.logging.level = WARNING
523
523
524 ## send logs only from erroneous/slow requests
524 ## send logs only from erroneous/slow requests
525 ## (saves API quota for intensive logging)
525 ## (saves API quota for intensive logging)
526 appenlight.logging_on_error = false
526 appenlight.logging_on_error = false
527
527
528 ## list of additonal keywords that should be grabbed from environ object
528 ## list of additonal keywords that should be grabbed from environ object
529 ## can be string with comma separated list of words in lowercase
529 ## can be string with comma separated list of words in lowercase
530 ## (by default client will always send following info:
530 ## (by default client will always send following info:
531 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
531 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
532 ## start with HTTP* this list be extended with additional keywords here
532 ## start with HTTP* this list be extended with additional keywords here
533 appenlight.environ_keys_whitelist =
533 appenlight.environ_keys_whitelist =
534
534
535 ## list of keywords that should be blanked from request object
535 ## list of keywords that should be blanked from request object
536 ## can be string with comma separated list of words in lowercase
536 ## can be string with comma separated list of words in lowercase
537 ## (by default client will always blank keys that contain following words
537 ## (by default client will always blank keys that contain following words
538 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
538 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
539 ## this list be extended with additional keywords set here
539 ## this list be extended with additional keywords set here
540 appenlight.request_keys_blacklist =
540 appenlight.request_keys_blacklist =
541
541
542 ## list of namespaces that should be ignores when gathering log entries
542 ## list of namespaces that should be ignores when gathering log entries
543 ## can be string with comma separated list of namespaces
543 ## can be string with comma separated list of namespaces
544 ## (by default the client ignores own entries: appenlight_client.client)
544 ## (by default the client ignores own entries: appenlight_client.client)
545 appenlight.log_namespace_blacklist =
545 appenlight.log_namespace_blacklist =
546
546
547
547
548 ################################################################################
548 ################################################################################
549 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
549 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
550 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
550 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
551 ## execute malicious code after an exception is raised. ##
551 ## execute malicious code after an exception is raised. ##
552 ################################################################################
552 ################################################################################
553 set debug = false
553 set debug = false
554
554
555
555
556 ##############
556 ##############
557 ## STYLING ##
557 ## STYLING ##
558 ##############
558 ##############
559 debug_style = false
559 debug_style = false
560
560
561 ###########################################
561 ###########################################
562 ### MAIN RHODECODE DATABASE CONFIG ###
562 ### MAIN RHODECODE DATABASE CONFIG ###
563 ###########################################
563 ###########################################
564 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
564 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
565 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode_test
565 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode_test
566 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode_test
566 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode_test
567 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
567 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
568
568
569 # see sqlalchemy docs for other advanced settings
569 # see sqlalchemy docs for other advanced settings
570
570
571 ## print the sql statements to output
571 ## print the sql statements to output
572 sqlalchemy.db1.echo = false
572 sqlalchemy.db1.echo = false
573 ## recycle the connections after this amount of seconds
573 ## recycle the connections after this amount of seconds
574 sqlalchemy.db1.pool_recycle = 3600
574 sqlalchemy.db1.pool_recycle = 3600
575 sqlalchemy.db1.convert_unicode = true
575 sqlalchemy.db1.convert_unicode = true
576
576
577 ## the number of connections to keep open inside the connection pool.
577 ## the number of connections to keep open inside the connection pool.
578 ## 0 indicates no limit
578 ## 0 indicates no limit
579 #sqlalchemy.db1.pool_size = 5
579 #sqlalchemy.db1.pool_size = 5
580
580
581 ## the number of connections to allow in connection pool "overflow", that is
581 ## the number of connections to allow in connection pool "overflow", that is
582 ## connections that can be opened above and beyond the pool_size setting,
582 ## connections that can be opened above and beyond the pool_size setting,
583 ## which defaults to five.
583 ## which defaults to five.
584 #sqlalchemy.db1.max_overflow = 10
584 #sqlalchemy.db1.max_overflow = 10
585
585
586
586
587 ##################
587 ##################
588 ### VCS CONFIG ###
588 ### VCS CONFIG ###
589 ##################
589 ##################
590 vcs.server.enable = true
590 vcs.server.enable = true
591 vcs.server = localhost:9901
591 vcs.server = localhost:9901
592
592
593 ## Web server connectivity protocol, responsible for web based VCS operatations
593 ## Web server connectivity protocol, responsible for web based VCS operatations
594 ## Available protocols are:
594 ## Available protocols are:
595 ## `http` - use http-rpc backend (default)
595 ## `http` - use http-rpc backend (default)
596 vcs.server.protocol = http
596 vcs.server.protocol = http
597
597
598 ## Push/Pull operations protocol, available options are:
598 ## Push/Pull operations protocol, available options are:
599 ## `http` - use http-rpc backend (default)
599 ## `http` - use http-rpc backend (default)
600 ## `vcsserver.scm_app` - internal app (EE only)
600 ## `vcsserver.scm_app` - internal app (EE only)
601 vcs.scm_app_implementation = http
601 vcs.scm_app_implementation = http
602
602
603 ## Push/Pull operations hooks protocol, available options are:
603 ## Push/Pull operations hooks protocol, available options are:
604 ## `http` - use http-rpc backend (default)
604 ## `http` - use http-rpc backend (default)
605 vcs.hooks.protocol = http
605 vcs.hooks.protocol = http
606
606
607 vcs.server.log_level = debug
607 vcs.server.log_level = debug
608 ## Start VCSServer with this instance as a subprocess, usefull for development
608 ## Start VCSServer with this instance as a subprocess, usefull for development
609 vcs.start_server = false
609 vcs.start_server = false
610
610
611 ## List of enabled VCS backends, available options are:
611 ## List of enabled VCS backends, available options are:
612 ## `hg` - mercurial
612 ## `hg` - mercurial
613 ## `git` - git
613 ## `git` - git
614 ## `svn` - subversion
614 ## `svn` - subversion
615 vcs.backends = hg, git, svn
615 vcs.backends = hg, git, svn
616
616
617 vcs.connection_timeout = 3600
617 vcs.connection_timeout = 3600
618 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
618 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
619 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
619 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
620 #vcs.svn.compatible_version = pre-1.8-compatible
620 #vcs.svn.compatible_version = pre-1.8-compatible
621
621
622
622
623 ############################################################
623 ############################################################
624 ### Subversion proxy support (mod_dav_svn) ###
624 ### Subversion proxy support (mod_dav_svn) ###
625 ### Maps RhodeCode repo groups into SVN paths for Apache ###
625 ### Maps RhodeCode repo groups into SVN paths for Apache ###
626 ############################################################
626 ############################################################
627 ## Enable or disable the config file generation.
627 ## Enable or disable the config file generation.
628 svn.proxy.generate_config = false
628 svn.proxy.generate_config = false
629 ## Generate config file with `SVNListParentPath` set to `On`.
629 ## Generate config file with `SVNListParentPath` set to `On`.
630 svn.proxy.list_parent_path = true
630 svn.proxy.list_parent_path = true
631 ## Set location and file name of generated config file.
631 ## Set location and file name of generated config file.
632 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
632 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
633 ## Used as a prefix to the `Location` block in the generated config file.
633 ## Used as a prefix to the `Location` block in the generated config file.
634 ## In most cases it should be set to `/`.
634 ## In most cases it should be set to `/`.
635 svn.proxy.location_root = /
635 svn.proxy.location_root = /
636 ## Command to reload the mod dav svn configuration on change.
636 ## Command to reload the mod dav svn configuration on change.
637 ## Example: `/etc/init.d/apache2 reload`
637 ## Example: `/etc/init.d/apache2 reload`
638 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
638 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
639 ## If the timeout expires before the reload command finishes, the command will
639 ## If the timeout expires before the reload command finishes, the command will
640 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
640 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
641 #svn.proxy.reload_timeout = 10
641 #svn.proxy.reload_timeout = 10
642
642
643 ############################################################
643 ############################################################
644 ### SSH Support Settings ###
644 ### SSH Support Settings ###
645 ############################################################
645 ############################################################
646
646
647 ## Defines if the authorized_keys file should be written on any change of
647 ## Defines if the authorized_keys file should be written on any change of
648 ## user ssh keys, setting this to false also disables posibility of adding
648 ## user ssh keys, setting this to false also disables posibility of adding
649 ## ssh keys for users from web interface.
649 ## ssh keys for users from web interface.
650 ssh.generate_authorized_keyfile = true
650 ssh.generate_authorized_keyfile = true
651
651
652 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
652 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
653 # ssh.authorized_keys_ssh_opts =
653 # ssh.authorized_keys_ssh_opts =
654
654
655 ## File to generate the authorized keys together with options
655 ## File to generate the authorized keys together with options
656 ## It is possible to have multiple key files specified in `sshd_config` e.g.
656 ## It is possible to have multiple key files specified in `sshd_config` e.g.
657 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
657 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
658 ssh.authorized_keys_file_path = %(here)s/rc/authorized_keys_rhodecode
658 ssh.authorized_keys_file_path = %(here)s/rc/authorized_keys_rhodecode
659
659
660 ## Command to execute the SSH wrapper. The binary is available in the
660 ## Command to execute the SSH wrapper. The binary is available in the
661 ## rhodecode installation directory.
661 ## rhodecode installation directory.
662 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
662 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
663 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
663 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
664
664
665 ## Allow shell when executing the ssh-wrapper command
665 ## Allow shell when executing the ssh-wrapper command
666 ssh.wrapper_cmd_allow_shell = false
666 ssh.wrapper_cmd_allow_shell = false
667
667
668 ## Enables logging, and detailed output send back to the client. Usefull for
668 ## Enables logging, and detailed output send back to the client. Usefull for
669 ## debugging, shouldn't be used in production.
669 ## debugging, shouldn't be used in production.
670 ssh.enable_debug_logging = false
670 ssh.enable_debug_logging = false
671
671
672 ## Paths to binary executrables, by default they are the names, but we can
672 ## Paths to binary executrables, by default they are the names, but we can
673 ## override them if we want to use a custom one
673 ## override them if we want to use a custom one
674 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
674 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
675 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
675 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
676 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
676 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
677
677
678
678
679 ## Dummy marker to add new entries after.
679 ## Dummy marker to add new entries after.
680 ## Add any custom entries below. Please don't remove.
680 ## Add any custom entries below. Please don't remove.
681 custom.conf = 1
681 custom.conf = 1
682
682
683
683
684 ################################
684 ################################
685 ### LOGGING CONFIGURATION ####
685 ### LOGGING CONFIGURATION ####
686 ################################
686 ################################
687 [loggers]
687 [loggers]
688 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper
688 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper
689
689
690 [handlers]
690 [handlers]
691 keys = console, console_sql
691 keys = console, console_sql
692
692
693 [formatters]
693 [formatters]
694 keys = generic, color_formatter, color_formatter_sql
694 keys = generic, color_formatter, color_formatter_sql
695
695
696 #############
696 #############
697 ## LOGGERS ##
697 ## LOGGERS ##
698 #############
698 #############
699 [logger_root]
699 [logger_root]
700 level = NOTSET
700 level = NOTSET
701 handlers = console
701 handlers = console
702
702
703 [logger_routes]
703 [logger_routes]
704 level = DEBUG
704 level = DEBUG
705 handlers =
705 handlers =
706 qualname = routes.middleware
706 qualname = routes.middleware
707 ## "level = DEBUG" logs the route matched and routing variables.
707 ## "level = DEBUG" logs the route matched and routing variables.
708 propagate = 1
708 propagate = 1
709
709
710 [logger_beaker]
710 [logger_beaker]
711 level = DEBUG
711 level = DEBUG
712 handlers =
712 handlers =
713 qualname = beaker.container
713 qualname = beaker.container
714 propagate = 1
714 propagate = 1
715
715
716 [logger_rhodecode]
716 [logger_rhodecode]
717 level = DEBUG
717 level = DEBUG
718 handlers =
718 handlers =
719 qualname = rhodecode
719 qualname = rhodecode
720 propagate = 1
720 propagate = 1
721
721
722 [logger_sqlalchemy]
722 [logger_sqlalchemy]
723 level = ERROR
723 level = ERROR
724 handlers = console_sql
724 handlers = console_sql
725 qualname = sqlalchemy.engine
725 qualname = sqlalchemy.engine
726 propagate = 0
726 propagate = 0
727
727
728 [logger_ssh_wrapper]
728 [logger_ssh_wrapper]
729 level = DEBUG
729 level = DEBUG
730 handlers =
730 handlers =
731 qualname = ssh_wrapper
731 qualname = ssh_wrapper
732 propagate = 1
732 propagate = 1
733
733
734
734
735 ##############
735 ##############
736 ## HANDLERS ##
736 ## HANDLERS ##
737 ##############
737 ##############
738
738
739 [handler_console]
739 [handler_console]
740 class = StreamHandler
740 class = StreamHandler
741 args = (sys.stderr,)
741 args = (sys.stderr,)
742 level = DEBUG
742 level = DEBUG
743 formatter = generic
743 formatter = generic
744
744
745 [handler_console_sql]
745 [handler_console_sql]
746 class = StreamHandler
746 class = StreamHandler
747 args = (sys.stderr,)
747 args = (sys.stderr,)
748 level = WARN
748 level = WARN
749 formatter = generic
749 formatter = generic
750
750
751 ################
751 ################
752 ## FORMATTERS ##
752 ## FORMATTERS ##
753 ################
753 ################
754
754
755 [formatter_generic]
755 [formatter_generic]
756 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
756 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
757 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
757 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
758 datefmt = %Y-%m-%d %H:%M:%S
758 datefmt = %Y-%m-%d %H:%M:%S
759
759
760 [formatter_color_formatter]
760 [formatter_color_formatter]
761 class = rhodecode.lib.logging_formatter.ColorFormatter
761 class = rhodecode.lib.logging_formatter.ColorFormatter
762 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
762 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
763 datefmt = %Y-%m-%d %H:%M:%S
763 datefmt = %Y-%m-%d %H:%M:%S
764
764
765 [formatter_color_formatter_sql]
765 [formatter_color_formatter_sql]
766 class = rhodecode.lib.logging_formatter.ColorFormatterSql
766 class = rhodecode.lib.logging_formatter.ColorFormatterSql
767 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
767 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
768 datefmt = %Y-%m-%d %H:%M:%S
768 datefmt = %Y-%m-%d %H:%M:%S
General Comments 0
You need to be logged in to leave comments. Login now