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