##// END OF EJS Templates
ssh: embedded ssh support...
marcink -
r2043:338dc54d default
parent child Browse files
Show More
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
This diff has been collapsed as it changes many lines, (607 lines changed) Show them Hide them
@@ -0,0 +1,607 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import os
22 import re
23 import sys
24 import json
25 import logging
26 import random
27 import signal
28 import tempfile
29 from subprocess import Popen, PIPE, check_output, CalledProcessError
30 import ConfigParser
31 import urllib2
32 import urlparse
33
34 import click
35 import pyramid.paster
36
37
38 log = logging.getLogger(__name__)
39
40
41 def setup_logging(ini_path, debug):
42 if debug:
43 # enabled rhodecode.ini controlled logging setup
44 pyramid.paster.setup_logging(ini_path)
45 else:
46 # configure logging in a mode that doesn't print anything.
47 # in case of regularly configured logging it gets printed out back
48 # to the client doing an SSH command.
49 logger = logging.getLogger('')
50 null = logging.NullHandler()
51 # add the handler to the root logger
52 logger.handlers = [null]
53
54
55 class SubversionTunnelWrapper(object):
56 process = None
57
58 def __init__(self, timeout, repositories_root=None, svn_path=None):
59 self.timeout = timeout
60 self.stdin = sys.stdin
61 self.repositories_root = repositories_root
62 self.svn_path = svn_path or 'svnserve'
63 self.svn_conf_fd, self.svn_conf_path = tempfile.mkstemp()
64 self.hooks_env_fd, self.hooks_env_path = tempfile.mkstemp()
65 self.read_only = False
66 self.create_svn_config()
67
68 def create_svn_config(self):
69 content = (
70 '[general]\n'
71 'hooks-env = {}\n').format(self.hooks_env_path)
72 with os.fdopen(self.svn_conf_fd, 'w') as config_file:
73 config_file.write(content)
74
75 def create_hooks_env(self):
76 content = (
77 '[default]\n'
78 'LANG = en_US.UTF-8\n')
79 if self.read_only:
80 content += 'SSH_READ_ONLY = 1\n'
81 with os.fdopen(self.hooks_env_fd, 'w') as hooks_env_file:
82 hooks_env_file.write(content)
83
84 def remove_configs(self):
85 os.remove(self.svn_conf_path)
86 os.remove(self.hooks_env_path)
87
88 def start(self):
89 config = ['--config-file', self.svn_conf_path]
90 command = [self.svn_path, '-t'] + config
91 if self.repositories_root:
92 command.extend(['-r', self.repositories_root])
93 self.process = Popen(command, stdin=PIPE)
94
95 def sync(self):
96 while self.process.poll() is None:
97 next_byte = self.stdin.read(1)
98 if not next_byte:
99 break
100 self.process.stdin.write(next_byte)
101 self.remove_configs()
102
103 @property
104 def return_code(self):
105 return self.process.returncode
106
107 def get_first_client_response(self):
108 signal.signal(signal.SIGALRM, self.interrupt)
109 signal.alarm(self.timeout)
110 first_response = self._read_first_client_response()
111 signal.alarm(0)
112 return (
113 self._parse_first_client_response(first_response)
114 if first_response else None)
115
116 def patch_first_client_response(self, response, **kwargs):
117 self.create_hooks_env()
118 data = response.copy()
119 data.update(kwargs)
120 data['url'] = self._svn_string(data['url'])
121 data['ra_client'] = self._svn_string(data['ra_client'])
122 data['client'] = data['client'] or ''
123 buffer_ = (
124 "( {version} ( {capabilities} ) {url}{ra_client}"
125 "( {client}) ) ".format(**data))
126 self.process.stdin.write(buffer_)
127
128 def fail(self, message):
129 print(
130 "( failure ( ( 210005 {message} 0: 0 ) ) )".format(
131 message=self._svn_string(message)))
132 self.remove_configs()
133 self.process.kill()
134
135 def interrupt(self, signum, frame):
136 self.fail("Exited by timeout")
137
138 def _svn_string(self, str_):
139 if not str_:
140 return ''
141 return '{length}:{string} '.format(length=len(str_), string=str_)
142
143 def _read_first_client_response(self):
144 buffer_ = ""
145 brackets_stack = []
146 while True:
147 next_byte = self.stdin.read(1)
148 buffer_ += next_byte
149 if next_byte == "(":
150 brackets_stack.append(next_byte)
151 elif next_byte == ")":
152 brackets_stack.pop()
153 elif next_byte == " " and not brackets_stack:
154 break
155 return buffer_
156
157 def _parse_first_client_response(self, buffer_):
158 """
159 According to the Subversion RA protocol, the first request
160 should look like:
161
162 ( version:number ( cap:word ... ) url:string ? ra-client:string
163 ( ? client:string ) )
164
165 Please check https://svn.apache.org/repos/asf/subversion/trunk/
166 subversion/libsvn_ra_svn/protocol
167 """
168 version_re = r'(?P<version>\d+)'
169 capabilities_re = r'\(\s(?P<capabilities>[\w\d\-\ ]+)\s\)'
170 url_re = r'\d+\:(?P<url>[\W\w]+)'
171 ra_client_re = r'(\d+\:(?P<ra_client>[\W\w]+)\s)'
172 client_re = r'(\d+\:(?P<client>[\W\w]+)\s)*'
173 regex = re.compile(
174 r'^\(\s{version}\s{capabilities}\s{url}\s{ra_client}'
175 r'\(\s{client}\)\s\)\s*$'.format(
176 version=version_re, capabilities=capabilities_re,
177 url=url_re, ra_client=ra_client_re, client=client_re))
178 matcher = regex.match(buffer_)
179 return matcher.groupdict() if matcher else None
180
181
182 class RhodeCodeApiClient(object):
183 def __init__(self, api_key, api_host):
184 self.api_key = api_key
185 self.api_host = api_host
186
187 if not api_host:
188 raise ValueError('api_key:{} not defined'.format(api_key))
189 if not api_host:
190 raise ValueError('api_host:{} not defined '.format(api_host))
191
192 def request(self, method, args):
193 id_ = random.randrange(1, 9999)
194 args = {
195 'id': id_,
196 'api_key': self.api_key,
197 'method': method,
198 'args': args
199 }
200 host = '{host}/_admin/api'.format(host=self.api_host)
201
202 log.debug('Doing API call to %s method:%s', host, method)
203 req = urllib2.Request(
204 host,
205 data=json.dumps(args),
206 headers={'content-type': 'text/plain'})
207 ret = urllib2.urlopen(req)
208 raw_json = ret.read()
209 json_data = json.loads(raw_json)
210 id_ret = json_data['id']
211
212 if id_ret != id_:
213 raise Exception('something went wrong. '
214 'ID mismatch got %s, expected %s | %s'
215 % (id_ret, id_, raw_json))
216
217 result = json_data['result']
218 error = json_data['error']
219 return result, error
220
221 def get_user_permissions(self, user, user_id):
222 result, error = self.request('get_user', {'userid': int(user_id)})
223 if result is None and error:
224 raise Exception(
225 'User "%s" not found or another error happened: %s!' % (
226 user, error))
227 log.debug(
228 'Given User: `%s` Fetched User: `%s`', user, result.get('username'))
229 return result.get('permissions').get('repositories')
230
231 def invalidate_cache(self, repo_name):
232 log.debug('Invalidate cache for repo:%s', repo_name)
233 return self.request('invalidate_cache', {'repoid': repo_name})
234
235 def get_repo_store(self):
236 result, error = self.request('get_repo_store', {})
237 return result
238
239
240 class VcsServer(object):
241
242 def __init__(self, user, user_permissions, config):
243 self.user = user
244 self.user_permissions = user_permissions
245 self.config = config
246 self.repo_name = None
247 self.repo_mode = None
248 self.store = {}
249 self.ini_path = ''
250
251 def run(self):
252 raise NotImplementedError()
253
254 def get_root_store(self):
255 root_store = self.store['path']
256 if not root_store.endswith('/'):
257 # always append trailing slash
258 root_store = root_store + '/'
259 return root_store
260
261
262 class MercurialServer(VcsServer):
263 read_only = False
264
265 def __init__(self, store, ini_path, repo_name,
266 user, user_permissions, config):
267 super(MercurialServer, self).__init__(user, user_permissions, config)
268 self.store = store
269 self.repo_name = repo_name
270 self.ini_path = ini_path
271 self.hg_path = config.get('app:main', 'ssh.executable.hg')
272
273 def run(self):
274 if not self._check_permissions():
275 return 2, False
276
277 tip_before = self.tip()
278 exit_code = os.system(self.command)
279 tip_after = self.tip()
280 return exit_code, tip_before != tip_after
281
282 def tip(self):
283 root = self.get_root_store()
284 command = (
285 'cd {root}; {hg_path} -R {root}{repo_name} tip --template "{{node}}\n"'
286 ''.format(
287 root=root, hg_path=self.hg_path, repo_name=self.repo_name))
288 try:
289 tip = check_output(command, shell=True).strip()
290 except CalledProcessError:
291 tip = None
292 return tip
293
294 @property
295 def command(self):
296 root = self.get_root_store()
297 arguments = (
298 '--config hooks.pretxnchangegroup=\"false\"'
299 if self.read_only else '')
300
301 command = (
302 "cd {root}; {hg_path} -R {root}{repo_name} serve --stdio"
303 " {arguments}".format(
304 root=root, hg_path=self.hg_path, repo_name=self.repo_name,
305 arguments=arguments))
306 log.debug("Final CMD: %s", command)
307 return command
308
309 def _check_permissions(self):
310 permission = self.user_permissions.get(self.repo_name)
311 if permission is None or permission == 'repository.none':
312 log.error('repo not found or no permissions')
313 return False
314
315 elif permission in ['repository.admin', 'repository.write']:
316 log.info(
317 'Write Permissions for User "%s" granted to repo "%s"!' % (
318 self.user, self.repo_name))
319 else:
320 self.read_only = True
321 log.info(
322 'Only Read Only access for User "%s" granted to repo "%s"!',
323 self.user, self.repo_name)
324 return True
325
326
327 class GitServer(VcsServer):
328 def __init__(self, store, ini_path, repo_name, repo_mode,
329 user, user_permissions, config):
330 super(GitServer, self).__init__(user, user_permissions, config)
331 self.store = store
332 self.ini_path = ini_path
333 self.repo_name = repo_name
334 self.repo_mode = repo_mode
335 self.git_path = config.get('app:main', 'ssh.executable.git')
336
337 def run(self):
338 exit_code = self._check_permissions()
339 if exit_code:
340 return exit_code, False
341
342 self._update_environment()
343 exit_code = os.system(self.command)
344 return exit_code, self.repo_mode == "receive-pack"
345
346 @property
347 def command(self):
348 root = self.get_root_store()
349 command = "cd {root}; {git_path}-{mode} '{root}{repo_name}'".format(
350 root=root, git_path=self.git_path, mode=self.repo_mode,
351 repo_name=self.repo_name)
352 log.debug("Final CMD: %s", command)
353 return command
354
355 def _update_environment(self):
356 action = "push" if self.repo_mode == "receive-pack" else "pull",
357 scm_data = {
358 "ip": os.environ["SSH_CLIENT"].split()[0],
359 "username": self.user,
360 "action": action,
361 "repository": self.repo_name,
362 "scm": "git",
363 "config": self.ini_path,
364 "make_lock": None,
365 "locked_by": [None, None]
366 }
367 os.putenv("RC_SCM_DATA", json.dumps(scm_data))
368
369 def _check_permissions(self):
370 permission = self.user_permissions.get(self.repo_name)
371 log.debug(
372 'permission for %s on %s are: %s',
373 self.user, self.repo_name, permission)
374
375 if permission is None or permission == 'repository.none':
376 log.error('repo not found or no permissions')
377 return 2
378 elif permission in ['repository.admin', 'repository.write']:
379 log.info(
380 'Write Permissions for User "%s" granted to repo "%s"!',
381 self.user, self.repo_name)
382 elif (permission == 'repository.read' and
383 self.repo_mode == 'upload-pack'):
384 log.info(
385 'Only Read Only access for User "%s" granted to repo "%s"!',
386 self.user, self.repo_name)
387 elif (permission == 'repository.read'
388 and self.repo_mode == 'receive-pack'):
389 log.error(
390 'Only Read Only access for User "%s" granted to repo "%s"!'
391 ' Failing!', self.user, self.repo_name)
392 return -3
393 else:
394 log.error('Cannot properly fetch user permission. '
395 'Return value is: %s', permission)
396 return -2
397
398
399 class SubversionServer(VcsServer):
400
401 def __init__(self, store, ini_path,
402 user, user_permissions, config):
403 super(SubversionServer, self).__init__(user, user_permissions, config)
404 self.store = store
405 self.ini_path = ini_path
406 # this is set in .run() from input stream
407 self.repo_name = None
408 self.svn_path = config.get('app:main', 'ssh.executable.svn')
409
410 def run(self):
411 root = self.get_root_store()
412 log.debug("Using subversion binaries from '%s'", self.svn_path)
413
414 self.tunnel = SubversionTunnelWrapper(
415 timeout=self.timeout, repositories_root=root, svn_path=self.svn_path)
416 self.tunnel.start()
417 first_response = self.tunnel.get_first_client_response()
418 if not first_response:
419 self.tunnel.fail("Repository name cannot be extracted")
420 return 1, False
421
422 url_parts = urlparse.urlparse(first_response['url'])
423 self.repo_name = url_parts.path.strip('/')
424 if not self._check_permissions():
425 self.tunnel.fail("Not enough permissions")
426 return 1, False
427
428 self.tunnel.patch_first_client_response(first_response)
429 self.tunnel.sync()
430 return self.tunnel.return_code, False
431
432 @property
433 def timeout(self):
434 timeout = 30
435 return timeout
436
437 def _check_permissions(self):
438 permission = self.user_permissions.get(self.repo_name)
439
440 if permission in ['repository.admin', 'repository.write']:
441 self.tunnel.read_only = False
442 return True
443
444 elif permission == 'repository.read':
445 self.tunnel.read_only = True
446 return True
447
448 else:
449 self.tunnel.fail("Not enough permissions for repository {}".format(
450 self.repo_name))
451 return False
452
453
454 class SshWrapper(object):
455
456 def __init__(self, command, mode, user, user_id, shell, ini_path):
457 self.command = command
458 self.mode = mode
459 self.user = user
460 self.user_id = user_id
461 self.shell = shell
462 self.ini_path = ini_path
463
464 self.config = self.parse_config(ini_path)
465 api_key = self.config.get('app:main', 'ssh.api_key')
466 api_host = self.config.get('app:main', 'ssh.api_host')
467 self.api = RhodeCodeApiClient(api_key, api_host)
468
469 def parse_config(self, config):
470 parser = ConfigParser.ConfigParser()
471 parser.read(config)
472 return parser
473
474 def get_repo_details(self, mode):
475 type_ = mode if mode in ['svn', 'hg', 'git'] else None
476 mode = mode
477 name = None
478
479 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
480 hg_match = re.match(hg_pattern, self.command)
481 if hg_match is not None:
482 type_ = 'hg'
483 name = hg_match.group(1).strip('/')
484 return type_, name, mode
485
486 git_pattern = (
487 r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
488 git_match = re.match(git_pattern, self.command)
489 if git_match is not None:
490 type_ = 'git'
491 name = git_match.group(2).strip('/')
492 mode = git_match.group(1)
493 return type_, name, mode
494
495 svn_pattern = r'^svnserve -t'
496 svn_match = re.match(svn_pattern, self.command)
497 if svn_match is not None:
498 type_ = 'svn'
499 # Repo name should be extracted from the input stream
500 return type_, name, mode
501
502 return type_, name, mode
503
504 def serve(self, vcs, repo, mode, user, permissions):
505 store = self.api.get_repo_store()
506
507 log.debug(
508 'VCS detected:`%s` mode: `%s` repo: %s', vcs, mode, repo)
509
510 if vcs == 'hg':
511 server = MercurialServer(
512 store=store, ini_path=self.ini_path,
513 repo_name=repo, user=user,
514 user_permissions=permissions, config=self.config)
515 return server.run()
516
517 elif vcs == 'git':
518 server = GitServer(
519 store=store, ini_path=self.ini_path,
520 repo_name=repo, repo_mode=mode, user=user,
521 user_permissions=permissions, config=self.config)
522 return server.run()
523
524 elif vcs == 'svn':
525 server = SubversionServer(
526 store=store, ini_path=self.ini_path,
527 user=user,
528 user_permissions=permissions, config=self.config)
529 return server.run()
530
531 else:
532 raise Exception('Unrecognised VCS: {}'.format(vcs))
533
534 def wrap(self):
535 mode = self.mode
536 user = self.user
537 user_id = self.user_id
538 shell = self.shell
539
540 scm_detected, scm_repo, scm_mode = self.get_repo_details(mode)
541 log.debug(
542 'Mode: `%s` User: `%s:%s` Shell: `%s` SSH Command: `\"%s\"` '
543 'SCM_DETECTED: `%s` SCM Mode: `%s` SCM Repo: `%s`',
544 mode, user, user_id, shell, self.command,
545 scm_detected, scm_mode, scm_repo)
546
547 try:
548 permissions = self.api.get_user_permissions(user, user_id)
549 except Exception as e:
550 log.exception('Failed to fetch user permissions')
551 return 1
552
553 if shell and self.command is None:
554 log.info(
555 'Dropping to shell, no command given and shell is allowed')
556 os.execl('/bin/bash', '-l')
557 exit_code = 1
558
559 elif scm_detected:
560 try:
561 exit_code, is_updated = self.serve(
562 scm_detected, scm_repo, scm_mode, user, permissions)
563 if exit_code == 0 and is_updated:
564 self.api.invalidate_cache(scm_repo)
565 except Exception:
566 log.exception('Error occurred during execution of SshWrapper')
567 exit_code = -1
568
569 elif self.command is None and shell is False:
570 log.error('No Command given.')
571 exit_code = -1
572
573 else:
574 log.error(
575 'Unhandled Command: "%s" Aborting.', self.command)
576 exit_code = -1
577
578 return exit_code
579
580
581 @click.command()
582 @click.argument('ini_path', type=click.Path(exists=True))
583 @click.option(
584 '--mode', '-m', required=False, default='auto',
585 type=click.Choice(['auto', 'vcs', 'git', 'hg', 'svn', 'test']),
586 help='mode of operation')
587 @click.option('--user', help='Username for which the command will be executed')
588 @click.option('--user-id', help='User ID for which the command will be executed')
589 @click.option('--shell', '-s', is_flag=True, help='Allow Shell')
590 @click.option('--debug', is_flag=True, help='Enabled detailed output logging')
591 def main(ini_path, mode, user, user_id, shell, debug):
592 setup_logging(ini_path, debug)
593
594 command = os.environ.get('SSH_ORIGINAL_COMMAND', '')
595 if not command and mode not in ['test']:
596 raise ValueError(
597 'Unable to fetch SSH_ORIGINAL_COMMAND from environment.'
598 'Please make sure this is set and available during execution '
599 'of this script.')
600
601 try:
602 ssh_wrapper = SshWrapper(command, mode, user, user_id, shell, ini_path)
603 except Exception:
604 log.exception('Failed to execute SshWrapper')
605 sys.exit(-5)
606
607 sys.exit(ssh_wrapper.wrap()) No newline at end of file
@@ -0,0 +1,195 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import json
22
23 import pytest
24 from mock import Mock, patch, call
25
26 from rhodecode.apps.ssh_support.lib.ssh_wrapper import GitServer
27
28
29 @pytest.fixture
30 def git_server():
31 return GitServerCreator()
32
33
34 class GitServerCreator(object):
35 root = '/tmp/repo/path/'
36 git_path = '/usr/local/bin/'
37 config_data = {
38 'app:main': {
39 'ssh.executable.git': git_path
40 }
41 }
42 repo_name = 'test_git'
43 repo_mode = 'receive-pack'
44 user = 'vcs'
45
46 def __init__(self):
47 def config_get(part, key):
48 return self.config_data.get(part, {}).get(key)
49 self.config_mock = Mock()
50 self.config_mock.get = Mock(side_effect=config_get)
51
52 def create(self, **kwargs):
53 parameters = {
54 'store': {'path': self.root},
55 'ini_path': '',
56 'user': self.user,
57 'repo_name': self.repo_name,
58 'repo_mode': self.repo_mode,
59 'user_permissions': {
60 self.repo_name: 'repo_admin'
61 },
62 'config': self.config_mock,
63 }
64 parameters.update(kwargs)
65 server = GitServer(**parameters)
66 return server
67
68
69 class TestGitServer(object):
70 def test_command(self, git_server):
71 server = git_server.create()
72 server.read_only = False
73 expected_command = (
74 'cd {root}; {git_path}-{repo_mode}'
75 ' \'{root}{repo_name}\''.format(
76 root=git_server.root, git_path=git_server.git_path,
77 repo_mode=git_server.repo_mode, repo_name=git_server.repo_name)
78 )
79 assert expected_command == server.command
80
81 def test_run_returns_exit_code_2_when_no_permissions(self, git_server, caplog):
82 server = git_server.create()
83 with patch.object(server, '_check_permissions') as permissions_mock:
84 with patch.object(server, '_update_environment'):
85 permissions_mock.return_value = 2
86 exit_code = server.run()
87
88 assert exit_code == (2, False)
89
90 def test_run_returns_executes_command(self, git_server, caplog):
91 server = git_server.create()
92 with patch.object(server, '_check_permissions') as permissions_mock:
93 with patch('os.system') as system_mock:
94 with patch.object(server, '_update_environment') as (
95 update_mock):
96 permissions_mock.return_value = 0
97 system_mock.return_value = 0
98 exit_code = server.run()
99
100 system_mock.assert_called_once_with(server.command)
101 update_mock.assert_called_once_with()
102
103 assert exit_code == (0, True)
104
105 @pytest.mark.parametrize(
106 'repo_mode, action', [
107 ['receive-pack', 'push'],
108 ['upload-pack', 'pull']
109 ])
110 def test_update_environment(self, git_server, repo_mode, action):
111 server = git_server.create(repo_mode=repo_mode)
112 with patch('os.environ', {'SSH_CLIENT': '10.10.10.10 b'}):
113 with patch('os.putenv') as putenv_mock:
114 server._update_environment()
115 expected_data = {
116 "username": git_server.user,
117 "scm": "git",
118 "repository": git_server.repo_name,
119 "make_lock": None,
120 "action": [action],
121 "ip": "10.10.10.10",
122 "locked_by": [None, None],
123 "config": ""
124 }
125 putenv_mock.assert_called_once_with(
126 'RC_SCM_DATA', json.dumps(expected_data))
127
128
129 class TestGitServerCheckPermissions(object):
130 def test_returns_2_when_no_permissions_found(self, git_server, caplog):
131 user_permissions = {}
132 server = git_server.create(user_permissions=user_permissions)
133 result = server._check_permissions()
134 assert result == 2
135
136 log_msg = 'permission for vcs on test_git are: None'
137 assert log_msg in [t[2] for t in caplog.record_tuples]
138
139 def test_returns_2_when_no_permissions(self, git_server, caplog):
140 user_permissions = {git_server.repo_name: 'repository.none'}
141 server = git_server.create(user_permissions=user_permissions)
142 result = server._check_permissions()
143 assert result == 2
144
145 log_msg = 'repo not found or no permissions'
146 assert log_msg in [t[2] for t in caplog.record_tuples]
147
148 @pytest.mark.parametrize(
149 'permission', ['repository.admin', 'repository.write'])
150 def test_access_allowed_when_user_has_write_permissions(
151 self, git_server, permission, caplog):
152 user_permissions = {git_server.repo_name: permission}
153 server = git_server.create(user_permissions=user_permissions)
154 result = server._check_permissions()
155 assert result is None
156
157 log_msg = 'Write Permissions for User "%s" granted to repo "%s"!' % (
158 git_server.user, git_server.repo_name)
159 assert log_msg in [t[2] for t in caplog.record_tuples]
160
161 def test_write_access_is_not_allowed_when_user_has_read_permission(
162 self, git_server, caplog):
163 user_permissions = {git_server.repo_name: 'repository.read'}
164 server = git_server.create(
165 user_permissions=user_permissions, repo_mode='receive-pack')
166 result = server._check_permissions()
167 assert result == -3
168
169 log_msg = 'Only Read Only access for User "%s" granted to repo "%s"! Failing!' % (
170 git_server.user, git_server.repo_name)
171 assert log_msg in [t[2] for t in caplog.record_tuples]
172
173 def test_read_access_allowed_when_user_has_read_permission(
174 self, git_server, caplog):
175 user_permissions = {git_server.repo_name: 'repository.read'}
176 server = git_server.create(
177 user_permissions=user_permissions, repo_mode='upload-pack')
178 result = server._check_permissions()
179 assert result is None
180
181 log_msg = 'Only Read Only access for User "%s" granted to repo "%s"!' % (
182 git_server.user, git_server.repo_name)
183 assert log_msg in [t[2] for t in caplog.record_tuples]
184
185 def test_returns_error_when_permission_not_recognised(
186 self, git_server, caplog):
187 user_permissions = {git_server.repo_name: 'repository.whatever'}
188 server = git_server.create(
189 user_permissions=user_permissions, repo_mode='upload-pack')
190 result = server._check_permissions()
191 assert result == -2
192
193 log_msg = 'Cannot properly fetch user permission. ' \
194 'Return value is: repository.whatever'
195 assert log_msg in [t[2] for t in caplog.record_tuples] No newline at end of file
@@ -0,0 +1,146 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22 from mock import Mock, patch, call
23
24 from rhodecode.apps.ssh_support.lib.ssh_wrapper import MercurialServer
25
26
27 @pytest.fixture
28 def hg_server():
29 return MercurialServerCreator()
30
31
32 class MercurialServerCreator(object):
33 root = '/tmp/repo/path/'
34 hg_path = '/usr/local/bin/hg'
35
36 config_data = {
37 'app:main': {
38 'ssh.executable.hg': hg_path
39 }
40 }
41 repo_name = 'test_hg'
42 user = 'vcs'
43
44 def __init__(self):
45 def config_get(part, key):
46 return self.config_data.get(part, {}).get(key)
47 self.config_mock = Mock()
48 self.config_mock.get = Mock(side_effect=config_get)
49
50 def create(self, **kwargs):
51 parameters = {
52 'store': {'path': self.root},
53 'ini_path': '',
54 'user': self.user,
55 'repo_name': self.repo_name,
56 'user_permissions': {
57 'test_hg': 'repo_admin'
58 },
59 'config': self.config_mock,
60 }
61 parameters.update(kwargs)
62 server = MercurialServer(**parameters)
63 return server
64
65
66 class TestMercurialServer(object):
67 def test_read_only_command(self, hg_server):
68 server = hg_server.create()
69 server.read_only = True
70 expected_command = (
71 'cd {root}; {hg_path} -R {root}{repo_name} serve --stdio'
72 ' --config hooks.pretxnchangegroup="false"'.format(
73 root=hg_server.root, hg_path=hg_server.hg_path,
74 repo_name=hg_server.repo_name)
75 )
76 assert expected_command == server.command
77
78 def test_normal_command(self, hg_server):
79 server = hg_server.create()
80 server.read_only = False
81 expected_command = (
82 'cd {root}; {hg_path} -R {root}{repo_name} serve --stdio '.format(
83 root=hg_server.root, hg_path=hg_server.hg_path,
84 repo_name=hg_server.repo_name)
85 )
86 assert expected_command == server.command
87
88 def test_access_rejected_when_permissions_are_not_found(self, hg_server, caplog):
89 user_permissions = {}
90 server = hg_server.create(user_permissions=user_permissions)
91 result = server._check_permissions()
92 assert result is False
93
94 log_msg = 'repo not found or no permissions'
95 assert log_msg in [t[2] for t in caplog.record_tuples]
96
97 def test_access_rejected_when_no_permissions(self, hg_server, caplog):
98 user_permissions = {hg_server.repo_name: 'repository.none'}
99 server = hg_server.create(user_permissions=user_permissions)
100 result = server._check_permissions()
101 assert result is False
102
103 log_msg = 'repo not found or no permissions'
104 assert log_msg in [t[2] for t in caplog.record_tuples]
105
106 @pytest.mark.parametrize(
107 'permission', ['repository.admin', 'repository.write'])
108 def test_access_allowed_when_user_has_write_permissions(
109 self, hg_server, permission, caplog):
110 user_permissions = {hg_server.repo_name: permission}
111 server = hg_server.create(user_permissions=user_permissions)
112 result = server._check_permissions()
113 assert result is True
114
115 assert server.read_only is False
116 log_msg = 'Write Permissions for User "vcs" granted to repo "test_hg"!'
117 assert log_msg in [t[2] for t in caplog.record_tuples]
118
119 def test_access_allowed_when_user_has_read_permissions(self, hg_server, caplog):
120 user_permissions = {hg_server.repo_name: 'repository.read'}
121 server = hg_server.create(user_permissions=user_permissions)
122 result = server._check_permissions()
123 assert result is True
124
125 assert server.read_only is True
126 log_msg = 'Only Read Only access for User "%s" granted to repo "%s"!' % (
127 hg_server.user, hg_server.repo_name)
128 assert log_msg in [t[2] for t in caplog.record_tuples]
129
130 def test_run_returns_exit_code_2_when_no_permissions(self, hg_server, caplog):
131 server = hg_server.create()
132 with patch.object(server, '_check_permissions') as permissions_mock:
133 permissions_mock.return_value = False
134 exit_code = server.run()
135 assert exit_code == (2, False)
136
137 def test_run_returns_executes_command(self, hg_server, caplog):
138 server = hg_server.create()
139 with patch.object(server, '_check_permissions') as permissions_mock:
140 with patch('os.system') as system_mock:
141 permissions_mock.return_value = True
142 system_mock.return_value = 0
143 exit_code = server.run()
144
145 system_mock.assert_called_once_with(server.command)
146 assert exit_code == (0, False)
@@ -0,0 +1,136 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22 from mock import Mock, patch, call
23
24 from rhodecode.apps.ssh_support.lib.ssh_wrapper import SubversionServer
25
26
27 @pytest.fixture
28 def svn_server():
29 return SubversionServerCreator()
30
31
32 class SubversionServerCreator(object):
33 root = '/tmp/repo/path/'
34 svn_path = '/usr/local/bin/svnserve'
35 config_data = {
36 'app:main': {
37 'ssh.executable.svn': svn_path
38 }
39 }
40 repo_name = 'test-svn'
41 user = 'vcs'
42
43 def __init__(self):
44 def config_get(part, key):
45 return self.config_data.get(part, {}).get(key)
46 self.config_mock = Mock()
47 self.config_mock.get = Mock(side_effect=config_get)
48
49 def create(self, **kwargs):
50 parameters = {
51 'store': {'path': self.root},
52 'ini_path': '',
53 'user': self.user,
54 'user_permissions': {
55 self.repo_name: 'repo_admin'
56 },
57 'config': self.config_mock,
58 }
59 parameters.update(kwargs)
60 server = SubversionServer(**parameters)
61 return server
62
63
64 class TestSubversionServer(object):
65 def test_timeout_returns_value_from_config(self, svn_server):
66 server = svn_server.create()
67 assert server.timeout == 30
68
69 @pytest.mark.parametrize(
70 'permission', ['repository.admin', 'repository.write'])
71 def test_check_permissions_with_write_permissions(
72 self, svn_server, permission):
73 user_permissions = {svn_server.repo_name: permission}
74 server = svn_server.create(user_permissions=user_permissions)
75 server.tunnel = Mock()
76 server.repo_name = svn_server.repo_name
77 result = server._check_permissions()
78 assert result is True
79 assert server.tunnel.read_only is False
80
81 def test_check_permissions_with_read_permissions(self, svn_server):
82 user_permissions = {svn_server.repo_name: 'repository.read'}
83 server = svn_server.create(user_permissions=user_permissions)
84 server.tunnel = Mock()
85 server.repo_name = svn_server.repo_name
86 result = server._check_permissions()
87 assert result is True
88 assert server.tunnel.read_only is True
89
90 def test_check_permissions_with_no_permissions(self, svn_server, caplog):
91 tunnel_mock = Mock()
92 user_permissions = {}
93 server = svn_server.create(user_permissions=user_permissions)
94 server.tunnel = tunnel_mock
95 server.repo_name = svn_server.repo_name
96 result = server._check_permissions()
97 assert result is False
98 tunnel_mock.fail.assert_called_once_with(
99 "Not enough permissions for repository {}".format(
100 svn_server.repo_name))
101
102 def test_run_returns_1_when_repository_name_cannot_be_extracted(
103 self, svn_server):
104 server = svn_server.create()
105 with patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.SubversionTunnelWrapper') as tunnel_mock:
106 tunnel_mock().get_first_client_response.return_value = None
107 exit_code = server.run()
108 assert exit_code == (1, False)
109 tunnel_mock().fail.assert_called_once_with(
110 'Repository name cannot be extracted')
111
112 def test_run_returns_tunnel_return_code(self, svn_server, caplog):
113 server = svn_server.create()
114 fake_response = {
115 'url': 'ssh+svn://test@example.com/test-svn/'
116 }
117 with patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.SubversionTunnelWrapper') as tunnel_mock:
118 with patch.object(server, '_check_permissions') as (
119 permissions_mock):
120 permissions_mock.return_value = True
121 tunnel = tunnel_mock()
122 tunnel.get_first_client_response.return_value = fake_response
123 tunnel.return_code = 0
124 exit_code = server.run()
125 permissions_mock.assert_called_once_with()
126
127 expected_log_calls = sorted([
128 "Using subversion binaries from '%s'" % svn_server.svn_path
129 ])
130
131 assert expected_log_calls == [t[2] for t in caplog.record_tuples]
132
133 assert exit_code == (0, False)
134 tunnel.patch_first_client_response.assert_called_once_with(
135 fake_response)
136 tunnel.sync.assert_called_once_with()
@@ -0,0 +1,204 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import os
21 import mock
22 import pytest
23 import ConfigParser
24
25 from rhodecode.apps.ssh_support.lib.ssh_wrapper import SshWrapper
26
27
28 @pytest.fixture
29 def dummy_conf(tmpdir):
30 conf = ConfigParser.ConfigParser()
31 conf.add_section('app:main')
32 conf.set('app:main', 'ssh.executable.hg', '/usr/bin/hg')
33 conf.set('app:main', 'ssh.executable.git', '/usr/bin/git')
34 conf.set('app:main', 'ssh.executable.svn', '/usr/bin/svnserve')
35
36 conf.set('app:main', 'ssh.api_key', 'xxx')
37 conf.set('app:main', 'ssh.api_host', 'http://localhost')
38
39 f_path = os.path.join(str(tmpdir), 'ssh_wrapper_test.ini')
40 with open(f_path, 'wb') as f:
41 conf.write(f)
42
43 return os.path.join(f_path)
44
45
46 class TestGetRepoDetails(object):
47 @pytest.mark.parametrize(
48 'command', [
49 'hg -R test-repo serve --stdio',
50 'hg -R test-repo serve --stdio'
51 ])
52 def test_hg_command_matched(self, command, dummy_conf):
53 wrapper = SshWrapper(command, 'auto', 'admin', '3', 'False', dummy_conf)
54 type_, name, mode = wrapper.get_repo_details('auto')
55 assert type_ == 'hg'
56 assert name == 'test-repo'
57 assert mode is 'auto'
58
59 @pytest.mark.parametrize(
60 'command', [
61 'hg test-repo serve --stdio',
62 'hg -R test-repo serve',
63 'hg serve --stdio',
64 'hg serve -R test-repo'
65 ])
66 def test_hg_command_not_matched(self, command, dummy_conf):
67 wrapper = SshWrapper(command, 'auto', 'admin', '3', 'False', dummy_conf)
68 type_, name, mode = wrapper.get_repo_details('auto')
69 assert type_ is None
70 assert name is None
71 assert mode is 'auto'
72
73
74 class TestServe(object):
75 def test_serve_raises_an_exception_when_vcs_is_not_recognized(self, dummy_conf):
76 with mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store'):
77 wrapper = SshWrapper('random command', 'auto', 'admin', '3', 'False', dummy_conf)
78
79 with pytest.raises(Exception) as exc_info:
80 wrapper.serve(
81 vcs='microsoft-tfs', repo='test-repo', mode=None, user='test',
82 permissions={})
83 assert exc_info.value.message == 'Unrecognised VCS: microsoft-tfs'
84
85
86 class TestServeHg(object):
87
88 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.invalidate_cache')
89 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_user_permissions')
90 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store')
91 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.MercurialServer.run')
92 def test_serve_creates_hg_instance(
93 self, mercurial_run_mock, get_repo_store_mock, get_user_mock,
94 invalidate_cache_mock, dummy_conf):
95
96 repo_name = None
97 mercurial_run_mock.return_value = 0, True
98 get_user_mock.return_value = {repo_name: 'repository.admin'}
99 get_repo_store_mock.return_value = {'path': '/tmp'}
100
101 wrapper = SshWrapper('date', 'hg', 'admin', '3', 'False',
102 dummy_conf)
103 exit_code = wrapper.wrap()
104 assert exit_code == 0
105 assert mercurial_run_mock.called
106
107 assert get_repo_store_mock.called
108 assert get_user_mock.called
109 invalidate_cache_mock.assert_called_once_with(repo_name)
110
111 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.invalidate_cache')
112 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_user_permissions')
113 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store')
114 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.MercurialServer.run')
115 def test_serve_hg_invalidates_cache(
116 self, mercurial_run_mock, get_repo_store_mock, get_user_mock,
117 invalidate_cache_mock, dummy_conf):
118
119 repo_name = None
120 mercurial_run_mock.return_value = 0, True
121 get_user_mock.return_value = {repo_name: 'repository.admin'}
122 get_repo_store_mock.return_value = {'path': '/tmp'}
123
124 wrapper = SshWrapper('date', 'hg', 'admin', '3', 'False',
125 dummy_conf)
126 exit_code = wrapper.wrap()
127 assert exit_code == 0
128 assert mercurial_run_mock.called
129
130 assert get_repo_store_mock.called
131 assert get_user_mock.called
132 invalidate_cache_mock.assert_called_once_with(repo_name)
133
134
135 class TestServeGit(object):
136
137 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.invalidate_cache')
138 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_user_permissions')
139 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store')
140 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.GitServer.run')
141 def test_serve_creates_git_instance(self, git_run_mock, get_repo_store_mock, get_user_mock,
142 invalidate_cache_mock, dummy_conf):
143 repo_name = None
144 git_run_mock.return_value = 0, True
145 get_user_mock.return_value = {repo_name: 'repository.admin'}
146 get_repo_store_mock.return_value = {'path': '/tmp'}
147
148 wrapper = SshWrapper('date', 'git', 'admin', '3', 'False',
149 dummy_conf)
150
151 exit_code = wrapper.wrap()
152 assert exit_code == 0
153 assert git_run_mock.called
154 assert get_repo_store_mock.called
155 assert get_user_mock.called
156 invalidate_cache_mock.assert_called_once_with(repo_name)
157
158 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.invalidate_cache')
159 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_user_permissions')
160 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store')
161 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.GitServer.run')
162 def test_serve_git_invalidates_cache(
163 self, git_run_mock, get_repo_store_mock, get_user_mock,
164 invalidate_cache_mock, dummy_conf):
165 repo_name = None
166 git_run_mock.return_value = 0, True
167 get_user_mock.return_value = {repo_name: 'repository.admin'}
168 get_repo_store_mock.return_value = {'path': '/tmp'}
169
170 wrapper = SshWrapper('date', 'git', 'admin', '3', 'False', dummy_conf)
171
172 exit_code = wrapper.wrap()
173 assert exit_code == 0
174 assert git_run_mock.called
175
176 assert get_repo_store_mock.called
177 assert get_user_mock.called
178 invalidate_cache_mock.assert_called_once_with(repo_name)
179
180
181 class TestServeSvn(object):
182
183 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.invalidate_cache')
184 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_user_permissions')
185 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.RhodeCodeApiClient.get_repo_store')
186 @mock.patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.SubversionServer.run')
187 def test_serve_creates_svn_instance(
188 self, svn_run_mock, get_repo_store_mock, get_user_mock,
189 invalidate_cache_mock, dummy_conf):
190
191 repo_name = None
192 svn_run_mock.return_value = 0, True
193 get_user_mock.return_value = {repo_name: 'repository.admin'}
194 get_repo_store_mock.return_value = {'path': '/tmp'}
195
196 wrapper = SshWrapper('date', 'svn', 'admin', '3', 'False', dummy_conf)
197
198 exit_code = wrapper.wrap()
199 assert exit_code == 0
200 assert svn_run_mock.called
201
202 assert get_repo_store_mock.called
203 assert get_user_mock.called
204 invalidate_cache_mock.assert_called_once_with(repo_name)
@@ -0,0 +1,285 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import subprocess
23 from io import BytesIO
24 from time import sleep
25
26 import pytest
27 from mock import patch, Mock, MagicMock, call
28
29 from rhodecode.apps.ssh_support.lib.ssh_wrapper import SubversionTunnelWrapper
30 from rhodecode.tests import no_newline_id_generator
31
32
33 class TestSubversionTunnelWrapper(object):
34 @pytest.mark.parametrize(
35 'input_string, output_string', [
36 [None, ''],
37 ['abcde', '5:abcde '],
38 ['abcdefghijk', '11:abcdefghijk ']
39 ])
40 def test_svn_string(self, input_string, output_string):
41 wrapper = SubversionTunnelWrapper(timeout=5)
42 assert wrapper._svn_string(input_string) == output_string
43
44 def test_read_first_client_response(self):
45 wrapper = SubversionTunnelWrapper(timeout=5)
46 buffer_ = '( abcd ( efg hij ) ) '
47 wrapper.stdin = BytesIO(buffer_)
48 result = wrapper._read_first_client_response()
49 assert result == buffer_
50
51 def test_parse_first_client_response_returns_dict(self):
52 response = (
53 '( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo'
54 ' log-revprops ) 26:svn+ssh://vcs@vm/hello-svn 38:SVN/1.8.11'
55 ' (x86_64-apple-darwin14.1.0) ( ) ) ')
56 wrapper = SubversionTunnelWrapper(timeout=5)
57 result = wrapper._parse_first_client_response(response)
58 assert result['version'] == '2'
59 assert (
60 result['capabilities'] ==
61 'edit-pipeline svndiff1 absent-entries depth mergeinfo'
62 ' log-revprops')
63 assert result['url'] == 'svn+ssh://vcs@vm/hello-svn'
64 assert result['ra_client'] == 'SVN/1.8.11 (x86_64-apple-darwin14.1.0)'
65 assert result['client'] is None
66
67 def test_parse_first_client_response_returns_none_when_not_matched(self):
68 response = (
69 '( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo'
70 ' log-revprops ) ) ')
71 wrapper = SubversionTunnelWrapper(timeout=5)
72 result = wrapper._parse_first_client_response(response)
73 assert result is None
74
75 def test_interrupt(self):
76 wrapper = SubversionTunnelWrapper(timeout=5)
77 with patch.object(wrapper, 'fail') as fail_mock:
78 wrapper.interrupt(1, 'frame')
79 fail_mock.assert_called_once_with("Exited by timeout")
80
81 def test_fail(self):
82 process_mock = Mock()
83 wrapper = SubversionTunnelWrapper(timeout=5)
84 with patch.object(wrapper, 'remove_configs') as remove_configs_mock:
85 with patch('sys.stdout', new_callable=BytesIO) as stdout_mock:
86 with patch.object(wrapper, 'process') as process_mock:
87 wrapper.fail('test message')
88 assert (
89 stdout_mock.getvalue() ==
90 '( failure ( ( 210005 12:test message 0: 0 ) ) )\n')
91 process_mock.kill.assert_called_once_with()
92 remove_configs_mock.assert_called_once_with()
93
94 @pytest.mark.parametrize(
95 'client, expected_client', [
96 ['test ', 'test '],
97 ['', ''],
98 [None, '']
99 ])
100 def test_client_in_patch_first_client_response(
101 self, client, expected_client):
102 response = {
103 'version': 2,
104 'capabilities': 'edit-pipeline svndiff1 absent-entries depth',
105 'url': 'svn+ssh://example.com/svn',
106 'ra_client': 'SVN/1.8.11 (x86_64-apple-darwin14.1.0)',
107 'client': client
108 }
109 wrapper = SubversionTunnelWrapper(timeout=5)
110 stdin = BytesIO()
111 with patch.object(wrapper, 'process') as process_mock:
112 process_mock.stdin = stdin
113 wrapper.patch_first_client_response(response)
114 assert (
115 stdin.getvalue() ==
116 '( 2 ( edit-pipeline svndiff1 absent-entries depth )'
117 ' 25:svn+ssh://example.com/svn 38:SVN/1.8.11'
118 ' (x86_64-apple-darwin14.1.0) ( {expected_client}) ) '.format(
119 expected_client=expected_client))
120
121 def test_kwargs_override_data_in_patch_first_client_response(self):
122 response = {
123 'version': 2,
124 'capabilities': 'edit-pipeline svndiff1 absent-entries depth',
125 'url': 'svn+ssh://example.com/svn',
126 'ra_client': 'SVN/1.8.11 (x86_64-apple-darwin14.1.0)',
127 'client': 'test'
128 }
129 wrapper = SubversionTunnelWrapper(timeout=5)
130 stdin = BytesIO()
131 with patch.object(wrapper, 'process') as process_mock:
132 process_mock.stdin = stdin
133 wrapper.patch_first_client_response(
134 response, version=3, client='abcde ',
135 capabilities='absent-entries depth',
136 url='svn+ssh://example.org/test',
137 ra_client='SVN/1.8.12 (ubuntu 14.04)')
138 assert (
139 stdin.getvalue() ==
140 '( 3 ( absent-entries depth ) 26:svn+ssh://example.org/test'
141 ' 25:SVN/1.8.12 (ubuntu 14.04) ( abcde ) ) ')
142
143 def test_patch_first_client_response_sets_environment(self):
144 response = {
145 'version': 2,
146 'capabilities': 'edit-pipeline svndiff1 absent-entries depth',
147 'url': 'svn+ssh://example.com/svn',
148 'ra_client': 'SVN/1.8.11 (x86_64-apple-darwin14.1.0)',
149 'client': 'test'
150 }
151 wrapper = SubversionTunnelWrapper(timeout=5)
152 stdin = BytesIO()
153 with patch.object(wrapper, 'create_hooks_env') as create_hooks_mock:
154 with patch.object(wrapper, 'process') as process_mock:
155 process_mock.stdin = stdin
156 wrapper.patch_first_client_response(response)
157 create_hooks_mock.assert_called_once_with()
158
159 def test_get_first_client_response_exits_by_signal(self):
160 wrapper = SubversionTunnelWrapper(timeout=1)
161 read_patch = patch.object(wrapper, '_read_first_client_response')
162 parse_patch = patch.object(wrapper, '_parse_first_client_response')
163 interrupt_patch = patch.object(wrapper, 'interrupt')
164
165 with read_patch as read_mock, parse_patch as parse_mock, \
166 interrupt_patch as interrupt_mock:
167 read_mock.side_effect = lambda: sleep(3)
168 wrapper.get_first_client_response()
169
170 assert parse_mock.call_count == 0
171 assert interrupt_mock.call_count == 1
172
173 def test_get_first_client_response_parses_data(self):
174 wrapper = SubversionTunnelWrapper(timeout=5)
175 response = (
176 '( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo'
177 ' log-revprops ) 26:svn+ssh://vcs@vm/hello-svn 38:SVN/1.8.11'
178 ' (x86_64-apple-darwin14.1.0) ( ) ) ')
179 read_patch = patch.object(wrapper, '_read_first_client_response')
180 parse_patch = patch.object(wrapper, '_parse_first_client_response')
181
182 with read_patch as read_mock, parse_patch as parse_mock:
183 read_mock.return_value = response
184 wrapper.get_first_client_response()
185
186 parse_mock.assert_called_once_with(response)
187
188 def test_return_code(self):
189 wrapper = SubversionTunnelWrapper(timeout=5)
190 with patch.object(wrapper, 'process') as process_mock:
191 process_mock.returncode = 1
192 assert wrapper.return_code == 1
193
194 def test_sync_loop_breaks_when_process_cannot_be_polled(self):
195 self.counter = 0
196 buffer_ = 'abcdefghij'
197
198 wrapper = SubversionTunnelWrapper(timeout=5)
199 wrapper.stdin = BytesIO(buffer_)
200 with patch.object(wrapper, 'remove_configs') as remove_configs_mock:
201 with patch.object(wrapper, 'process') as process_mock:
202 process_mock.poll.side_effect = self._poll
203 process_mock.stdin = BytesIO()
204 wrapper.sync()
205 assert process_mock.stdin.getvalue() == 'abcde'
206 remove_configs_mock.assert_called_once_with()
207
208 def test_sync_loop_breaks_when_nothing_to_read(self):
209 self.counter = 0
210 buffer_ = 'abcdefghij'
211
212 wrapper = SubversionTunnelWrapper(timeout=5)
213 wrapper.stdin = BytesIO(buffer_)
214 with patch.object(wrapper, 'remove_configs') as remove_configs_mock:
215 with patch.object(wrapper, 'process') as process_mock:
216 process_mock.poll.return_value = None
217 process_mock.stdin = BytesIO()
218 wrapper.sync()
219 assert process_mock.stdin.getvalue() == buffer_
220 remove_configs_mock.assert_called_once_with()
221
222 def test_start_without_repositories_root(self):
223 svn_path = '/usr/local/bin/svnserve'
224 wrapper = SubversionTunnelWrapper(timeout=5, svn_path=svn_path)
225 with patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.Popen') as popen_mock:
226 wrapper.start()
227 expected_command = [
228 svn_path, '-t', '--config-file', wrapper.svn_conf_path]
229 popen_mock.assert_called_once_with(
230 expected_command, stdin=subprocess.PIPE)
231 assert wrapper.process == popen_mock()
232
233 def test_start_with_repositories_root(self):
234 svn_path = '/usr/local/bin/svnserve'
235 repositories_root = '/home/repos'
236 wrapper = SubversionTunnelWrapper(
237 timeout=5, svn_path=svn_path, repositories_root=repositories_root)
238 with patch('rhodecode.apps.ssh_support.lib.ssh_wrapper.Popen') as popen_mock:
239 wrapper.start()
240 expected_command = [
241 svn_path, '-t', '--config-file', wrapper.svn_conf_path,
242 '-r', repositories_root]
243 popen_mock.assert_called_once_with(
244 expected_command, stdin=subprocess.PIPE)
245 assert wrapper.process == popen_mock()
246
247 def test_create_svn_config(self):
248 wrapper = SubversionTunnelWrapper(timeout=5)
249 file_mock = MagicMock(spec=file)
250 with patch('os.fdopen', create=True) as open_mock:
251 open_mock.return_value = file_mock
252 wrapper.create_svn_config()
253 open_mock.assert_called_once_with(wrapper.svn_conf_fd, 'w')
254 expected_content = '[general]\nhooks-env = {}\n'.format(
255 wrapper.hooks_env_path)
256 file_handle = file_mock.__enter__.return_value
257 file_handle.write.assert_called_once_with(expected_content)
258
259 @pytest.mark.parametrize(
260 'read_only, expected_content', [
261 [True, '[default]\nLANG = en_US.UTF-8\nSSH_READ_ONLY = 1\n'],
262 [False, '[default]\nLANG = en_US.UTF-8\n']
263 ], ids=no_newline_id_generator)
264 def test_create_hooks_env(self, read_only, expected_content):
265 wrapper = SubversionTunnelWrapper(timeout=5)
266 wrapper.read_only = read_only
267 file_mock = MagicMock(spec=file)
268 with patch('os.fdopen', create=True) as open_mock:
269 open_mock.return_value = file_mock
270 wrapper.create_hooks_env()
271 open_mock.assert_called_once_with(wrapper.hooks_env_fd, 'w')
272 file_handle = file_mock.__enter__.return_value
273 file_handle.write.assert_called_once_with(expected_content)
274
275 def test_remove_configs(self):
276 wrapper = SubversionTunnelWrapper(timeout=5)
277 with patch('os.remove') as remove_mock:
278 wrapper.remove_configs()
279 expected_calls = [
280 call(wrapper.svn_conf_path), call(wrapper.hooks_env_path)]
281 assert sorted(remove_mock.call_args_list) == sorted(expected_calls)
282
283 def _poll(self):
284 self.counter += 1
285 return None if self.counter < 6 else 1
@@ -1,703 +1,732 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 = 127.0.0.1
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
94 94 ## prefix middleware for RhodeCode.
95 95 ## recommended when using proxy setup.
96 96 ## allows to set RhodeCode under a prefix in server.
97 97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 98 ## And set your prefix like: `prefix = /custom_prefix`
99 99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 100 ## to make your cookies only work on prefix url
101 101 [filter:proxy-prefix]
102 102 use = egg:PasteDeploy#prefix
103 103 prefix = /
104 104
105 105 [app:main]
106 106 use = egg:rhodecode-enterprise-ce
107 107
108 108 ## enable proxy prefix middleware, defined above
109 109 #filter-with = proxy-prefix
110 110
111 111 # During development the we want to have the debug toolbar enabled
112 112 pyramid.includes =
113 113 pyramid_debugtoolbar
114 114 rhodecode.utils.debugtoolbar
115 115 rhodecode.lib.middleware.request_wrapper
116 116
117 117 pyramid.reload_templates = true
118 118
119 119 debugtoolbar.hosts = 0.0.0.0/0
120 120 debugtoolbar.exclude_prefixes =
121 121 /css
122 122 /fonts
123 123 /images
124 124 /js
125 125
126 126 ## RHODECODE PLUGINS ##
127 127 rhodecode.includes =
128 128 rhodecode.api
129 129
130 130
131 131 # api prefix url
132 132 rhodecode.api.url = /_admin/api
133 133
134 134
135 135 ## END RHODECODE PLUGINS ##
136 136
137 137 ## encryption key used to encrypt social plugin tokens,
138 138 ## remote_urls with credentials etc, if not set it defaults to
139 139 ## `beaker.session.secret`
140 140 #rhodecode.encrypted_values.secret =
141 141
142 142 ## decryption strict mode (enabled by default). It controls if decryption raises
143 143 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
144 144 #rhodecode.encrypted_values.strict = false
145 145
146 146 ## return gzipped responses from Rhodecode (static files/application)
147 147 gzip_responses = false
148 148
149 149 ## autogenerate javascript routes file on startup
150 150 generate_js_files = false
151 151
152 152 ## Optional Languages
153 153 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
154 154 lang = en
155 155
156 156 ## perform a full repository scan on each server start, this should be
157 157 ## set to false after first startup, to allow faster server restarts.
158 158 startup.import_repos = false
159 159
160 160 ## Uncomment and set this path to use archive download cache.
161 161 ## Once enabled, generated archives will be cached at this location
162 162 ## and served from the cache during subsequent requests for the same archive of
163 163 ## the repository.
164 164 #archive_cache_dir = /tmp/tarballcache
165 165
166 166 ## change this to unique ID for security
167 167 app_instance_uuid = rc-production
168 168
169 169 ## cut off limit for large diffs (size in bytes)
170 170 cut_off_limit_diff = 1024000
171 171 cut_off_limit_file = 256000
172 172
173 173 ## use cache version of scm repo everywhere
174 174 vcs_full_cache = true
175 175
176 176 ## force https in RhodeCode, fixes https redirects, assumes it's always https
177 177 ## Normally this is controlled by proper http flags sent from http server
178 178 force_https = false
179 179
180 180 ## use Strict-Transport-Security headers
181 181 use_htsts = false
182 182
183 183 ## number of commits stats will parse on each iteration
184 184 commit_parse_limit = 25
185 185
186 186 ## git rev filter option, --all is the default filter, if you need to
187 187 ## hide all refs in changelog switch this to --branches --tags
188 188 git_rev_filter = --branches --tags
189 189
190 190 # Set to true if your repos are exposed using the dumb protocol
191 191 git_update_server_info = false
192 192
193 193 ## RSS/ATOM feed options
194 194 rss_cut_off_limit = 256000
195 195 rss_items_per_page = 10
196 196 rss_include_diff = false
197 197
198 198 ## gist URL alias, used to create nicer urls for gist. This should be an
199 199 ## url that does rewrites to _admin/gists/{gistid}.
200 200 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
201 201 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
202 202 gist_alias_url =
203 203
204 204 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
205 205 ## used for access.
206 206 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
207 207 ## came from the the logged in user who own this authentication token.
208 208 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
209 209 ## authentication token. Such view would be only accessible when used together
210 210 ## with this authentication token
211 211 ##
212 212 ## list of all views can be found under `/_admin/permissions/auth_token_access`
213 213 ## The list should be "," separated and on a single line.
214 214 ##
215 215 ## Most common views to enable:
216 216 # RepoCommitsView:repo_commit_download
217 217 # RepoCommitsView:repo_commit_patch
218 218 # RepoCommitsView:repo_commit_raw
219 219 # RepoCommitsView:repo_commit_raw@TOKEN
220 220 # RepoFilesView:repo_files_diff
221 221 # RepoFilesView:repo_archivefile
222 222 # RepoFilesView:repo_file_raw
223 223 # GistView:*
224 224 api_access_controllers_whitelist =
225 225
226 226 ## default encoding used to convert from and to unicode
227 227 ## can be also a comma separated list of encoding in case of mixed encodings
228 228 default_encoding = UTF-8
229 229
230 230 ## instance-id prefix
231 231 ## a prefix key for this instance used for cache invalidation when running
232 232 ## multiple instances of rhodecode, make sure it's globally unique for
233 233 ## all running rhodecode instances. Leave empty if you don't use it
234 234 instance_id =
235 235
236 236 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
237 237 ## of an authentication plugin also if it is disabled by it's settings.
238 238 ## This could be useful if you are unable to log in to the system due to broken
239 239 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
240 240 ## module to log in again and fix the settings.
241 241 ##
242 242 ## Available builtin plugin IDs (hash is part of the ID):
243 243 ## egg:rhodecode-enterprise-ce#rhodecode
244 244 ## egg:rhodecode-enterprise-ce#pam
245 245 ## egg:rhodecode-enterprise-ce#ldap
246 246 ## egg:rhodecode-enterprise-ce#jasig_cas
247 247 ## egg:rhodecode-enterprise-ce#headers
248 248 ## egg:rhodecode-enterprise-ce#crowd
249 249 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
250 250
251 251 ## alternative return HTTP header for failed authentication. Default HTTP
252 252 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
253 253 ## handling that causing a series of failed authentication calls.
254 254 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
255 255 ## This will be served instead of default 401 on bad authnetication
256 256 auth_ret_code =
257 257
258 258 ## use special detection method when serving auth_ret_code, instead of serving
259 259 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
260 260 ## and then serve auth_ret_code to clients
261 261 auth_ret_code_detection = false
262 262
263 263 ## locking return code. When repository is locked return this HTTP code. 2XX
264 264 ## codes don't break the transactions while 4XX codes do
265 265 lock_ret_code = 423
266 266
267 267 ## allows to change the repository location in settings page
268 268 allow_repo_location_change = true
269 269
270 270 ## allows to setup custom hooks in settings page
271 271 allow_custom_hooks_settings = true
272 272
273 273 ## generated license token, goto license page in RhodeCode settings to obtain
274 274 ## new token
275 275 license_token =
276 276
277 277 ## supervisor connection uri, for managing supervisor and logs.
278 278 supervisor.uri =
279 279 ## supervisord group name/id we only want this RC instance to handle
280 280 supervisor.group_id = dev
281 281
282 282 ## Display extended labs settings
283 283 labs_settings_active = true
284 284
285 285 ####################################
286 286 ### CELERY CONFIG ####
287 287 ####################################
288 288 use_celery = false
289 289 broker.host = localhost
290 290 broker.vhost = rabbitmqhost
291 291 broker.port = 5672
292 292 broker.user = rabbitmq
293 293 broker.password = qweqwe
294 294
295 295 celery.imports = rhodecode.lib.celerylib.tasks
296 296
297 297 celery.result.backend = amqp
298 298 celery.result.dburi = amqp://
299 299 celery.result.serialier = json
300 300
301 301 #celery.send.task.error.emails = true
302 302 #celery.amqp.task.result.expires = 18000
303 303
304 304 celeryd.concurrency = 2
305 305 #celeryd.log.file = celeryd.log
306 306 celeryd.log.level = debug
307 307 celeryd.max.tasks.per.child = 1
308 308
309 309 ## tasks will never be sent to the queue, but executed locally instead.
310 310 celery.always.eager = false
311 311
312 312 ####################################
313 313 ### BEAKER CACHE ####
314 314 ####################################
315 315 # default cache dir for templates. Putting this into a ramdisk
316 316 ## can boost performance, eg. %(here)s/data_ramdisk
317 317 cache_dir = %(here)s/data
318 318
319 319 ## locking and default file storage for Beaker. Putting this into a ramdisk
320 320 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
321 321 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
322 322 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
323 323
324 324 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
325 325
326 326 beaker.cache.super_short_term.type = memory
327 327 beaker.cache.super_short_term.expire = 10
328 328 beaker.cache.super_short_term.key_length = 256
329 329
330 330 beaker.cache.short_term.type = memory
331 331 beaker.cache.short_term.expire = 60
332 332 beaker.cache.short_term.key_length = 256
333 333
334 334 beaker.cache.long_term.type = memory
335 335 beaker.cache.long_term.expire = 36000
336 336 beaker.cache.long_term.key_length = 256
337 337
338 338 beaker.cache.sql_cache_short.type = memory
339 339 beaker.cache.sql_cache_short.expire = 10
340 340 beaker.cache.sql_cache_short.key_length = 256
341 341
342 342 ## default is memory cache, configure only if required
343 343 ## using multi-node or multi-worker setup
344 344 #beaker.cache.auth_plugins.type = ext:database
345 345 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
346 346 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
347 347 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
348 348 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
349 349 #beaker.cache.auth_plugins.sa.pool_size = 10
350 350 #beaker.cache.auth_plugins.sa.max_overflow = 0
351 351
352 352 beaker.cache.repo_cache_long.type = memorylru_base
353 353 beaker.cache.repo_cache_long.max_items = 4096
354 354 beaker.cache.repo_cache_long.expire = 2592000
355 355
356 356 ## default is memorylru_base cache, configure only if required
357 357 ## using multi-node or multi-worker setup
358 358 #beaker.cache.repo_cache_long.type = ext:memcached
359 359 #beaker.cache.repo_cache_long.url = localhost:11211
360 360 #beaker.cache.repo_cache_long.expire = 1209600
361 361 #beaker.cache.repo_cache_long.key_length = 256
362 362
363 363 ####################################
364 364 ### BEAKER SESSION ####
365 365 ####################################
366 366
367 367 ## .session.type is type of storage options for the session, current allowed
368 368 ## types are file, ext:memcached, ext:database, and memory (default).
369 369 beaker.session.type = file
370 370 beaker.session.data_dir = %(here)s/data/sessions/data
371 371
372 372 ## db based session, fast, and allows easy management over logged in users
373 373 #beaker.session.type = ext:database
374 374 #beaker.session.table_name = db_session
375 375 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
376 376 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
377 377 #beaker.session.sa.pool_recycle = 3600
378 378 #beaker.session.sa.echo = false
379 379
380 380 beaker.session.key = rhodecode
381 381 beaker.session.secret = develop-rc-uytcxaz
382 382 beaker.session.lock_dir = %(here)s/data/sessions/lock
383 383
384 384 ## Secure encrypted cookie. Requires AES and AES python libraries
385 385 ## you must disable beaker.session.secret to use this
386 386 #beaker.session.encrypt_key = key_for_encryption
387 387 #beaker.session.validate_key = validation_key
388 388
389 389 ## sets session as invalid(also logging out user) if it haven not been
390 390 ## accessed for given amount of time in seconds
391 391 beaker.session.timeout = 2592000
392 392 beaker.session.httponly = true
393 393 ## Path to use for the cookie. Set to prefix if you use prefix middleware
394 394 #beaker.session.cookie_path = /custom_prefix
395 395
396 396 ## uncomment for https secure cookie
397 397 beaker.session.secure = false
398 398
399 399 ## auto save the session to not to use .save()
400 400 beaker.session.auto = false
401 401
402 402 ## default cookie expiration time in seconds, set to `true` to set expire
403 403 ## at browser close
404 404 #beaker.session.cookie_expires = 3600
405 405
406 406 ###################################
407 407 ## SEARCH INDEXING CONFIGURATION ##
408 408 ###################################
409 409 ## Full text search indexer is available in rhodecode-tools under
410 410 ## `rhodecode-tools index` command
411 411
412 412 ## WHOOSH Backend, doesn't require additional services to run
413 413 ## it works good with few dozen repos
414 414 search.module = rhodecode.lib.index.whoosh
415 415 search.location = %(here)s/data/index
416 416
417 417 ########################################
418 418 ### CHANNELSTREAM CONFIG ####
419 419 ########################################
420 420 ## channelstream enables persistent connections and live notification
421 421 ## in the system. It's also used by the chat system
422 422 channelstream.enabled = false
423 423
424 424 ## server address for channelstream server on the backend
425 425 channelstream.server = 127.0.0.1:9800
426 426
427 427 ## location of the channelstream server from outside world
428 428 ## use ws:// for http or wss:// for https. This address needs to be handled
429 429 ## by external HTTP server such as Nginx or Apache
430 430 ## see nginx/apache configuration examples in our docs
431 431 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
432 432 channelstream.secret = secret
433 433 channelstream.history.location = %(here)s/channelstream_history
434 434
435 435 ## Internal application path that Javascript uses to connect into.
436 436 ## If you use proxy-prefix the prefix should be added before /_channelstream
437 437 channelstream.proxy_path = /_channelstream
438 438
439 439
440 440 ###################################
441 441 ## APPENLIGHT CONFIG ##
442 442 ###################################
443 443
444 444 ## Appenlight is tailored to work with RhodeCode, see
445 445 ## http://appenlight.com for details how to obtain an account
446 446
447 447 ## appenlight integration enabled
448 448 appenlight = false
449 449
450 450 appenlight.server_url = https://api.appenlight.com
451 451 appenlight.api_key = YOUR_API_KEY
452 452 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
453 453
454 454 # used for JS client
455 455 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
456 456
457 457 ## TWEAK AMOUNT OF INFO SENT HERE
458 458
459 459 ## enables 404 error logging (default False)
460 460 appenlight.report_404 = false
461 461
462 462 ## time in seconds after request is considered being slow (default 1)
463 463 appenlight.slow_request_time = 1
464 464
465 465 ## record slow requests in application
466 466 ## (needs to be enabled for slow datastore recording and time tracking)
467 467 appenlight.slow_requests = true
468 468
469 469 ## enable hooking to application loggers
470 470 appenlight.logging = true
471 471
472 472 ## minimum log level for log capture
473 473 appenlight.logging.level = WARNING
474 474
475 475 ## send logs only from erroneous/slow requests
476 476 ## (saves API quota for intensive logging)
477 477 appenlight.logging_on_error = false
478 478
479 479 ## list of additonal keywords that should be grabbed from environ object
480 480 ## can be string with comma separated list of words in lowercase
481 481 ## (by default client will always send following info:
482 482 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
483 483 ## start with HTTP* this list be extended with additional keywords here
484 484 appenlight.environ_keys_whitelist =
485 485
486 486 ## list of keywords that should be blanked from request object
487 487 ## can be string with comma separated list of words in lowercase
488 488 ## (by default client will always blank keys that contain following words
489 489 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
490 490 ## this list be extended with additional keywords set here
491 491 appenlight.request_keys_blacklist =
492 492
493 493 ## list of namespaces that should be ignores when gathering log entries
494 494 ## can be string with comma separated list of namespaces
495 495 ## (by default the client ignores own entries: appenlight_client.client)
496 496 appenlight.log_namespace_blacklist =
497 497
498 498
499 499 ################################################################################
500 500 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
501 501 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
502 502 ## execute malicious code after an exception is raised. ##
503 503 ################################################################################
504 504 #set debug = false
505 505
506 506
507 507 ##############
508 508 ## STYLING ##
509 509 ##############
510 510 debug_style = true
511 511
512 512 ###########################################
513 513 ### MAIN RHODECODE DATABASE CONFIG ###
514 514 ###########################################
515 515 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
516 516 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
517 517 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
518 518 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
519 519
520 520 # see sqlalchemy docs for other advanced settings
521 521
522 522 ## print the sql statements to output
523 523 sqlalchemy.db1.echo = false
524 524 ## recycle the connections after this amount of seconds
525 525 sqlalchemy.db1.pool_recycle = 3600
526 526 sqlalchemy.db1.convert_unicode = true
527 527
528 528 ## the number of connections to keep open inside the connection pool.
529 529 ## 0 indicates no limit
530 530 #sqlalchemy.db1.pool_size = 5
531 531
532 532 ## the number of connections to allow in connection pool "overflow", that is
533 533 ## connections that can be opened above and beyond the pool_size setting,
534 534 ## which defaults to five.
535 535 #sqlalchemy.db1.max_overflow = 10
536 536
537 537
538 538 ##################
539 539 ### VCS CONFIG ###
540 540 ##################
541 541 vcs.server.enable = true
542 542 vcs.server = localhost:9900
543 543
544 544 ## Web server connectivity protocol, responsible for web based VCS operatations
545 545 ## Available protocols are:
546 546 ## `http` - use http-rpc backend (default)
547 547 vcs.server.protocol = http
548 548
549 549 ## Push/Pull operations protocol, available options are:
550 550 ## `http` - use http-rpc backend (default)
551 551 ##
552 552 vcs.scm_app_implementation = http
553 553
554 554 ## Push/Pull operations hooks protocol, available options are:
555 555 ## `http` - use http-rpc backend (default)
556 556 vcs.hooks.protocol = http
557 557
558 558 vcs.server.log_level = debug
559 559 ## Start VCSServer with this instance as a subprocess, usefull for development
560 560 vcs.start_server = true
561 561
562 562 ## List of enabled VCS backends, available options are:
563 563 ## `hg` - mercurial
564 564 ## `git` - git
565 565 ## `svn` - subversion
566 566 vcs.backends = hg, git, svn
567 567
568 568 vcs.connection_timeout = 3600
569 569 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
570 570 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
571 571 #vcs.svn.compatible_version = pre-1.8-compatible
572 572
573 573
574 574 ############################################################
575 575 ### Subversion proxy support (mod_dav_svn) ###
576 576 ### Maps RhodeCode repo groups into SVN paths for Apache ###
577 577 ############################################################
578 578 ## Enable or disable the config file generation.
579 579 svn.proxy.generate_config = false
580 580 ## Generate config file with `SVNListParentPath` set to `On`.
581 581 svn.proxy.list_parent_path = true
582 582 ## Set location and file name of generated config file.
583 583 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
584 584 ## Used as a prefix to the `Location` block in the generated config file.
585 585 ## In most cases it should be set to `/`.
586 586 svn.proxy.location_root = /
587 587 ## Command to reload the mod dav svn configuration on change.
588 588 ## Example: `/etc/init.d/apache2 reload`
589 589 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
590 590 ## If the timeout expires before the reload command finishes, the command will
591 591 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
592 592 #svn.proxy.reload_timeout = 10
593 593
594 594 ############################################################
595 595 ### SSH Support Settings ###
596 596 ############################################################
597 597
598 598 ## Defines if the authorized_keys file should be written on any change of
599 599 ## user ssh keys
600 600 ssh.generate_authorized_keyfile = false
601 601
602 602 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
603 603 # ssh.authorized_keys_ssh_opts =
604 604
605 605 ## File to generate the authorized keys together with options
606 ssh.authorized_keys_file_path = /home/USER/.ssh/authorized_keys
606 ## It is possible to have multiple key files specified in `sshd_config` e.g.
607 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
608 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
609
610 ## Command to execute the SSH wrapper. The binary is available in the
611 ## rhodecode installation directory.
612 ## e.g ~/.rccontrol/community-1/profile/bin/rcssh-wrapper
613 ssh.wrapper_cmd = ~/.rccontrol/community-1/rcssh-wrapper
614
615 ## Allow shell when executing the ssh-wrapper command
616 ssh.wrapper_cmd_allow_shell = false
607 617
608 ## Command to execute as an SSH wrapper, available from
609 ## https://code.rhodecode.com/rhodecode-ssh
610 ssh.wrapper_cmd = /home/USER/rhodecode-ssh/sshwrapper.py
618 ## Enables logging, and detailed output send back to the client. Usefull for
619 ## debugging, shouldn't be used in production.
620 ssh.enable_debug_logging = false
621
622 ## API KEY for user who has access to fetch other user permission information
623 ## most likely an super-admin account with some IP restrictions.
624 ssh.api_key =
611 625
612 ## Allow shell when executing the command
613 ssh.wrapper_cmd_allow_shell = false
626 ## API Host, the server address of RhodeCode instance that the api_key will
627 ## access
628 ssh.api_host = http://localhost
629
630 ## Paths to binary executrables, by default they are the names, but we can
631 ## override them if we want to use a custom one
632 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
633 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
634 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
635
614 636
615 637 ## Dummy marker to add new entries after.
616 638 ## Add any custom entries below. Please don't remove.
617 639 custom.conf = 1
618 640
619 641
620 642 ################################
621 643 ### LOGGING CONFIGURATION ####
622 644 ################################
623 645 [loggers]
624 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
646 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, ssh_wrapper
625 647
626 648 [handlers]
627 649 keys = console, console_sql
628 650
629 651 [formatters]
630 652 keys = generic, color_formatter, color_formatter_sql
631 653
632 654 #############
633 655 ## LOGGERS ##
634 656 #############
635 657 [logger_root]
636 658 level = NOTSET
637 659 handlers = console
638 660
639 661 [logger_routes]
640 662 level = DEBUG
641 663 handlers =
642 664 qualname = routes.middleware
643 665 ## "level = DEBUG" logs the route matched and routing variables.
644 666 propagate = 1
645 667
646 668 [logger_beaker]
647 669 level = DEBUG
648 670 handlers =
649 671 qualname = beaker.container
650 672 propagate = 1
651 673
652 674 [logger_templates]
653 675 level = INFO
654 676 handlers =
655 677 qualname = pylons.templating
656 678 propagate = 1
657 679
658 680 [logger_rhodecode]
659 681 level = DEBUG
660 682 handlers =
661 683 qualname = rhodecode
662 684 propagate = 1
663 685
664 686 [logger_sqlalchemy]
665 687 level = INFO
666 688 handlers = console_sql
667 689 qualname = sqlalchemy.engine
668 690 propagate = 0
669 691
692 [logger_ssh_wrapper]
693 level = DEBUG
694 handlers =
695 qualname = ssh_wrapper
696 propagate = 1
697
698
670 699 ##############
671 700 ## HANDLERS ##
672 701 ##############
673 702
674 703 [handler_console]
675 704 class = StreamHandler
676 705 args = (sys.stderr, )
677 706 level = DEBUG
678 707 formatter = color_formatter
679 708
680 709 [handler_console_sql]
681 710 class = StreamHandler
682 711 args = (sys.stderr, )
683 712 level = DEBUG
684 713 formatter = color_formatter_sql
685 714
686 715 ################
687 716 ## FORMATTERS ##
688 717 ################
689 718
690 719 [formatter_generic]
691 720 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
692 721 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
693 722 datefmt = %Y-%m-%d %H:%M:%S
694 723
695 724 [formatter_color_formatter]
696 725 class = rhodecode.lib.logging_formatter.ColorFormatter
697 726 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
698 727 datefmt = %Y-%m-%d %H:%M:%S
699 728
700 729 [formatter_color_formatter_sql]
701 730 class = rhodecode.lib.logging_formatter.ColorFormatterSql
702 731 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
703 732 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,672 +1,701 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 = 127.0.0.1
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
94 94 ## prefix middleware for RhodeCode.
95 95 ## recommended when using proxy setup.
96 96 ## allows to set RhodeCode under a prefix in server.
97 97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 98 ## And set your prefix like: `prefix = /custom_prefix`
99 99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 100 ## to make your cookies only work on prefix url
101 101 [filter:proxy-prefix]
102 102 use = egg:PasteDeploy#prefix
103 103 prefix = /
104 104
105 105 [app:main]
106 106 use = egg:rhodecode-enterprise-ce
107 107
108 108 ## enable proxy prefix middleware, defined above
109 109 #filter-with = proxy-prefix
110 110
111 111 ## encryption key used to encrypt social plugin tokens,
112 112 ## remote_urls with credentials etc, if not set it defaults to
113 113 ## `beaker.session.secret`
114 114 #rhodecode.encrypted_values.secret =
115 115
116 116 ## decryption strict mode (enabled by default). It controls if decryption raises
117 117 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
118 118 #rhodecode.encrypted_values.strict = false
119 119
120 120 ## return gzipped responses from Rhodecode (static files/application)
121 121 gzip_responses = false
122 122
123 123 ## autogenerate javascript routes file on startup
124 124 generate_js_files = false
125 125
126 126 ## Optional Languages
127 127 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
128 128 lang = en
129 129
130 130 ## perform a full repository scan on each server start, this should be
131 131 ## set to false after first startup, to allow faster server restarts.
132 132 startup.import_repos = false
133 133
134 134 ## Uncomment and set this path to use archive download cache.
135 135 ## Once enabled, generated archives will be cached at this location
136 136 ## and served from the cache during subsequent requests for the same archive of
137 137 ## the repository.
138 138 #archive_cache_dir = /tmp/tarballcache
139 139
140 140 ## change this to unique ID for security
141 141 app_instance_uuid = rc-production
142 142
143 143 ## cut off limit for large diffs (size in bytes)
144 144 cut_off_limit_diff = 1024000
145 145 cut_off_limit_file = 256000
146 146
147 147 ## use cache version of scm repo everywhere
148 148 vcs_full_cache = true
149 149
150 150 ## force https in RhodeCode, fixes https redirects, assumes it's always https
151 151 ## Normally this is controlled by proper http flags sent from http server
152 152 force_https = false
153 153
154 154 ## use Strict-Transport-Security headers
155 155 use_htsts = false
156 156
157 157 ## number of commits stats will parse on each iteration
158 158 commit_parse_limit = 25
159 159
160 160 ## git rev filter option, --all is the default filter, if you need to
161 161 ## hide all refs in changelog switch this to --branches --tags
162 162 git_rev_filter = --branches --tags
163 163
164 164 # Set to true if your repos are exposed using the dumb protocol
165 165 git_update_server_info = false
166 166
167 167 ## RSS/ATOM feed options
168 168 rss_cut_off_limit = 256000
169 169 rss_items_per_page = 10
170 170 rss_include_diff = false
171 171
172 172 ## gist URL alias, used to create nicer urls for gist. This should be an
173 173 ## url that does rewrites to _admin/gists/{gistid}.
174 174 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
175 175 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
176 176 gist_alias_url =
177 177
178 178 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
179 179 ## used for access.
180 180 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
181 181 ## came from the the logged in user who own this authentication token.
182 182 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
183 183 ## authentication token. Such view would be only accessible when used together
184 184 ## with this authentication token
185 185 ##
186 186 ## list of all views can be found under `/_admin/permissions/auth_token_access`
187 187 ## The list should be "," separated and on a single line.
188 188 ##
189 189 ## Most common views to enable:
190 190 # RepoCommitsView:repo_commit_download
191 191 # RepoCommitsView:repo_commit_patch
192 192 # RepoCommitsView:repo_commit_raw
193 193 # RepoCommitsView:repo_commit_raw@TOKEN
194 194 # RepoFilesView:repo_files_diff
195 195 # RepoFilesView:repo_archivefile
196 196 # RepoFilesView:repo_file_raw
197 197 # GistView:*
198 198 api_access_controllers_whitelist =
199 199
200 200 ## default encoding used to convert from and to unicode
201 201 ## can be also a comma separated list of encoding in case of mixed encodings
202 202 default_encoding = UTF-8
203 203
204 204 ## instance-id prefix
205 205 ## a prefix key for this instance used for cache invalidation when running
206 206 ## multiple instances of rhodecode, make sure it's globally unique for
207 207 ## all running rhodecode instances. Leave empty if you don't use it
208 208 instance_id =
209 209
210 210 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
211 211 ## of an authentication plugin also if it is disabled by it's settings.
212 212 ## This could be useful if you are unable to log in to the system due to broken
213 213 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
214 214 ## module to log in again and fix the settings.
215 215 ##
216 216 ## Available builtin plugin IDs (hash is part of the ID):
217 217 ## egg:rhodecode-enterprise-ce#rhodecode
218 218 ## egg:rhodecode-enterprise-ce#pam
219 219 ## egg:rhodecode-enterprise-ce#ldap
220 220 ## egg:rhodecode-enterprise-ce#jasig_cas
221 221 ## egg:rhodecode-enterprise-ce#headers
222 222 ## egg:rhodecode-enterprise-ce#crowd
223 223 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
224 224
225 225 ## alternative return HTTP header for failed authentication. Default HTTP
226 226 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
227 227 ## handling that causing a series of failed authentication calls.
228 228 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
229 229 ## This will be served instead of default 401 on bad authnetication
230 230 auth_ret_code =
231 231
232 232 ## use special detection method when serving auth_ret_code, instead of serving
233 233 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
234 234 ## and then serve auth_ret_code to clients
235 235 auth_ret_code_detection = false
236 236
237 237 ## locking return code. When repository is locked return this HTTP code. 2XX
238 238 ## codes don't break the transactions while 4XX codes do
239 239 lock_ret_code = 423
240 240
241 241 ## allows to change the repository location in settings page
242 242 allow_repo_location_change = true
243 243
244 244 ## allows to setup custom hooks in settings page
245 245 allow_custom_hooks_settings = true
246 246
247 247 ## generated license token, goto license page in RhodeCode settings to obtain
248 248 ## new token
249 249 license_token =
250 250
251 251 ## supervisor connection uri, for managing supervisor and logs.
252 252 supervisor.uri =
253 253 ## supervisord group name/id we only want this RC instance to handle
254 254 supervisor.group_id = prod
255 255
256 256 ## Display extended labs settings
257 257 labs_settings_active = true
258 258
259 259 ####################################
260 260 ### CELERY CONFIG ####
261 261 ####################################
262 262 use_celery = false
263 263 broker.host = localhost
264 264 broker.vhost = rabbitmqhost
265 265 broker.port = 5672
266 266 broker.user = rabbitmq
267 267 broker.password = qweqwe
268 268
269 269 celery.imports = rhodecode.lib.celerylib.tasks
270 270
271 271 celery.result.backend = amqp
272 272 celery.result.dburi = amqp://
273 273 celery.result.serialier = json
274 274
275 275 #celery.send.task.error.emails = true
276 276 #celery.amqp.task.result.expires = 18000
277 277
278 278 celeryd.concurrency = 2
279 279 #celeryd.log.file = celeryd.log
280 280 celeryd.log.level = debug
281 281 celeryd.max.tasks.per.child = 1
282 282
283 283 ## tasks will never be sent to the queue, but executed locally instead.
284 284 celery.always.eager = false
285 285
286 286 ####################################
287 287 ### BEAKER CACHE ####
288 288 ####################################
289 289 # default cache dir for templates. Putting this into a ramdisk
290 290 ## can boost performance, eg. %(here)s/data_ramdisk
291 291 cache_dir = %(here)s/data
292 292
293 293 ## locking and default file storage for Beaker. Putting this into a ramdisk
294 294 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
295 295 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
296 296 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
297 297
298 298 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
299 299
300 300 beaker.cache.super_short_term.type = memory
301 301 beaker.cache.super_short_term.expire = 10
302 302 beaker.cache.super_short_term.key_length = 256
303 303
304 304 beaker.cache.short_term.type = memory
305 305 beaker.cache.short_term.expire = 60
306 306 beaker.cache.short_term.key_length = 256
307 307
308 308 beaker.cache.long_term.type = memory
309 309 beaker.cache.long_term.expire = 36000
310 310 beaker.cache.long_term.key_length = 256
311 311
312 312 beaker.cache.sql_cache_short.type = memory
313 313 beaker.cache.sql_cache_short.expire = 10
314 314 beaker.cache.sql_cache_short.key_length = 256
315 315
316 316 ## default is memory cache, configure only if required
317 317 ## using multi-node or multi-worker setup
318 318 #beaker.cache.auth_plugins.type = ext:database
319 319 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
320 320 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
321 321 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
322 322 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
323 323 #beaker.cache.auth_plugins.sa.pool_size = 10
324 324 #beaker.cache.auth_plugins.sa.max_overflow = 0
325 325
326 326 beaker.cache.repo_cache_long.type = memorylru_base
327 327 beaker.cache.repo_cache_long.max_items = 4096
328 328 beaker.cache.repo_cache_long.expire = 2592000
329 329
330 330 ## default is memorylru_base cache, configure only if required
331 331 ## using multi-node or multi-worker setup
332 332 #beaker.cache.repo_cache_long.type = ext:memcached
333 333 #beaker.cache.repo_cache_long.url = localhost:11211
334 334 #beaker.cache.repo_cache_long.expire = 1209600
335 335 #beaker.cache.repo_cache_long.key_length = 256
336 336
337 337 ####################################
338 338 ### BEAKER SESSION ####
339 339 ####################################
340 340
341 341 ## .session.type is type of storage options for the session, current allowed
342 342 ## types are file, ext:memcached, ext:database, and memory (default).
343 343 beaker.session.type = file
344 344 beaker.session.data_dir = %(here)s/data/sessions/data
345 345
346 346 ## db based session, fast, and allows easy management over logged in users
347 347 #beaker.session.type = ext:database
348 348 #beaker.session.table_name = db_session
349 349 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
350 350 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
351 351 #beaker.session.sa.pool_recycle = 3600
352 352 #beaker.session.sa.echo = false
353 353
354 354 beaker.session.key = rhodecode
355 355 beaker.session.secret = production-rc-uytcxaz
356 356 beaker.session.lock_dir = %(here)s/data/sessions/lock
357 357
358 358 ## Secure encrypted cookie. Requires AES and AES python libraries
359 359 ## you must disable beaker.session.secret to use this
360 360 #beaker.session.encrypt_key = key_for_encryption
361 361 #beaker.session.validate_key = validation_key
362 362
363 363 ## sets session as invalid(also logging out user) if it haven not been
364 364 ## accessed for given amount of time in seconds
365 365 beaker.session.timeout = 2592000
366 366 beaker.session.httponly = true
367 367 ## Path to use for the cookie. Set to prefix if you use prefix middleware
368 368 #beaker.session.cookie_path = /custom_prefix
369 369
370 370 ## uncomment for https secure cookie
371 371 beaker.session.secure = false
372 372
373 373 ## auto save the session to not to use .save()
374 374 beaker.session.auto = false
375 375
376 376 ## default cookie expiration time in seconds, set to `true` to set expire
377 377 ## at browser close
378 378 #beaker.session.cookie_expires = 3600
379 379
380 380 ###################################
381 381 ## SEARCH INDEXING CONFIGURATION ##
382 382 ###################################
383 383 ## Full text search indexer is available in rhodecode-tools under
384 384 ## `rhodecode-tools index` command
385 385
386 386 ## WHOOSH Backend, doesn't require additional services to run
387 387 ## it works good with few dozen repos
388 388 search.module = rhodecode.lib.index.whoosh
389 389 search.location = %(here)s/data/index
390 390
391 391 ########################################
392 392 ### CHANNELSTREAM CONFIG ####
393 393 ########################################
394 394 ## channelstream enables persistent connections and live notification
395 395 ## in the system. It's also used by the chat system
396 396 channelstream.enabled = false
397 397
398 398 ## server address for channelstream server on the backend
399 399 channelstream.server = 127.0.0.1:9800
400 400
401 401 ## location of the channelstream server from outside world
402 402 ## use ws:// for http or wss:// for https. This address needs to be handled
403 403 ## by external HTTP server such as Nginx or Apache
404 404 ## see nginx/apache configuration examples in our docs
405 405 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
406 406 channelstream.secret = secret
407 407 channelstream.history.location = %(here)s/channelstream_history
408 408
409 409 ## Internal application path that Javascript uses to connect into.
410 410 ## If you use proxy-prefix the prefix should be added before /_channelstream
411 411 channelstream.proxy_path = /_channelstream
412 412
413 413
414 414 ###################################
415 415 ## APPENLIGHT CONFIG ##
416 416 ###################################
417 417
418 418 ## Appenlight is tailored to work with RhodeCode, see
419 419 ## http://appenlight.com for details how to obtain an account
420 420
421 421 ## appenlight integration enabled
422 422 appenlight = false
423 423
424 424 appenlight.server_url = https://api.appenlight.com
425 425 appenlight.api_key = YOUR_API_KEY
426 426 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
427 427
428 428 # used for JS client
429 429 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
430 430
431 431 ## TWEAK AMOUNT OF INFO SENT HERE
432 432
433 433 ## enables 404 error logging (default False)
434 434 appenlight.report_404 = false
435 435
436 436 ## time in seconds after request is considered being slow (default 1)
437 437 appenlight.slow_request_time = 1
438 438
439 439 ## record slow requests in application
440 440 ## (needs to be enabled for slow datastore recording and time tracking)
441 441 appenlight.slow_requests = true
442 442
443 443 ## enable hooking to application loggers
444 444 appenlight.logging = true
445 445
446 446 ## minimum log level for log capture
447 447 appenlight.logging.level = WARNING
448 448
449 449 ## send logs only from erroneous/slow requests
450 450 ## (saves API quota for intensive logging)
451 451 appenlight.logging_on_error = false
452 452
453 453 ## list of additonal keywords that should be grabbed from environ object
454 454 ## can be string with comma separated list of words in lowercase
455 455 ## (by default client will always send following info:
456 456 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
457 457 ## start with HTTP* this list be extended with additional keywords here
458 458 appenlight.environ_keys_whitelist =
459 459
460 460 ## list of keywords that should be blanked from request object
461 461 ## can be string with comma separated list of words in lowercase
462 462 ## (by default client will always blank keys that contain following words
463 463 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
464 464 ## this list be extended with additional keywords set here
465 465 appenlight.request_keys_blacklist =
466 466
467 467 ## list of namespaces that should be ignores when gathering log entries
468 468 ## can be string with comma separated list of namespaces
469 469 ## (by default the client ignores own entries: appenlight_client.client)
470 470 appenlight.log_namespace_blacklist =
471 471
472 472
473 473 ################################################################################
474 474 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
475 475 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
476 476 ## execute malicious code after an exception is raised. ##
477 477 ################################################################################
478 478 set debug = false
479 479
480 480
481 481 ###########################################
482 482 ### MAIN RHODECODE DATABASE CONFIG ###
483 483 ###########################################
484 484 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
485 485 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
486 486 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
487 487 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
488 488
489 489 # see sqlalchemy docs for other advanced settings
490 490
491 491 ## print the sql statements to output
492 492 sqlalchemy.db1.echo = false
493 493 ## recycle the connections after this amount of seconds
494 494 sqlalchemy.db1.pool_recycle = 3600
495 495 sqlalchemy.db1.convert_unicode = true
496 496
497 497 ## the number of connections to keep open inside the connection pool.
498 498 ## 0 indicates no limit
499 499 #sqlalchemy.db1.pool_size = 5
500 500
501 501 ## the number of connections to allow in connection pool "overflow", that is
502 502 ## connections that can be opened above and beyond the pool_size setting,
503 503 ## which defaults to five.
504 504 #sqlalchemy.db1.max_overflow = 10
505 505
506 506
507 507 ##################
508 508 ### VCS CONFIG ###
509 509 ##################
510 510 vcs.server.enable = true
511 511 vcs.server = localhost:9900
512 512
513 513 ## Web server connectivity protocol, responsible for web based VCS operatations
514 514 ## Available protocols are:
515 515 ## `http` - use http-rpc backend (default)
516 516 vcs.server.protocol = http
517 517
518 518 ## Push/Pull operations protocol, available options are:
519 519 ## `http` - use http-rpc backend (default)
520 520 ##
521 521 vcs.scm_app_implementation = http
522 522
523 523 ## Push/Pull operations hooks protocol, available options are:
524 524 ## `http` - use http-rpc backend (default)
525 525 vcs.hooks.protocol = http
526 526
527 527 vcs.server.log_level = info
528 528 ## Start VCSServer with this instance as a subprocess, usefull for development
529 529 vcs.start_server = false
530 530
531 531 ## List of enabled VCS backends, available options are:
532 532 ## `hg` - mercurial
533 533 ## `git` - git
534 534 ## `svn` - subversion
535 535 vcs.backends = hg, git, svn
536 536
537 537 vcs.connection_timeout = 3600
538 538 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
539 539 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
540 540 #vcs.svn.compatible_version = pre-1.8-compatible
541 541
542 542
543 543 ############################################################
544 544 ### Subversion proxy support (mod_dav_svn) ###
545 545 ### Maps RhodeCode repo groups into SVN paths for Apache ###
546 546 ############################################################
547 547 ## Enable or disable the config file generation.
548 548 svn.proxy.generate_config = false
549 549 ## Generate config file with `SVNListParentPath` set to `On`.
550 550 svn.proxy.list_parent_path = true
551 551 ## Set location and file name of generated config file.
552 552 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
553 553 ## Used as a prefix to the `Location` block in the generated config file.
554 554 ## In most cases it should be set to `/`.
555 555 svn.proxy.location_root = /
556 556 ## Command to reload the mod dav svn configuration on change.
557 557 ## Example: `/etc/init.d/apache2 reload`
558 558 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
559 559 ## If the timeout expires before the reload command finishes, the command will
560 560 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
561 561 #svn.proxy.reload_timeout = 10
562 562
563 563 ############################################################
564 564 ### SSH Support Settings ###
565 565 ############################################################
566 566
567 567 ## Defines if the authorized_keys file should be written on any change of
568 568 ## user ssh keys
569 569 ssh.generate_authorized_keyfile = false
570 570
571 571 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
572 572 # ssh.authorized_keys_ssh_opts =
573 573
574 574 ## File to generate the authorized keys together with options
575 ssh.authorized_keys_file_path = /home/USER/.ssh/authorized_keys
575 ## It is possible to have multiple key files specified in `sshd_config` e.g.
576 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
577 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
578
579 ## Command to execute the SSH wrapper. The binary is available in the
580 ## rhodecode installation directory.
581 ## e.g ~/.rccontrol/community-1/profile/bin/rcssh-wrapper
582 ssh.wrapper_cmd = ~/.rccontrol/community-1/rcssh-wrapper
583
584 ## Allow shell when executing the ssh-wrapper command
585 ssh.wrapper_cmd_allow_shell = false
576 586
577 ## Command to execute as an SSH wrapper, available from
578 ## https://code.rhodecode.com/rhodecode-ssh
579 ssh.wrapper_cmd = /home/USER/rhodecode-ssh/sshwrapper.py
587 ## Enables logging, and detailed output send back to the client. Usefull for
588 ## debugging, shouldn't be used in production.
589 ssh.enable_debug_logging = false
590
591 ## API KEY for user who has access to fetch other user permission information
592 ## most likely an super-admin account with some IP restrictions.
593 ssh.api_key =
580 594
581 ## Allow shell when executing the command
582 ssh.wrapper_cmd_allow_shell = false
595 ## API Host, the server address of RhodeCode instance that the api_key will
596 ## access
597 ssh.api_host = http://localhost
598
599 ## Paths to binary executrables, by default they are the names, but we can
600 ## override them if we want to use a custom one
601 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
602 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
603 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
604
583 605
584 606 ## Dummy marker to add new entries after.
585 607 ## Add any custom entries below. Please don't remove.
586 608 custom.conf = 1
587 609
588 610
589 611 ################################
590 612 ### LOGGING CONFIGURATION ####
591 613 ################################
592 614 [loggers]
593 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
615 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, ssh_wrapper
594 616
595 617 [handlers]
596 618 keys = console, console_sql
597 619
598 620 [formatters]
599 621 keys = generic, color_formatter, color_formatter_sql
600 622
601 623 #############
602 624 ## LOGGERS ##
603 625 #############
604 626 [logger_root]
605 627 level = NOTSET
606 628 handlers = console
607 629
608 630 [logger_routes]
609 631 level = DEBUG
610 632 handlers =
611 633 qualname = routes.middleware
612 634 ## "level = DEBUG" logs the route matched and routing variables.
613 635 propagate = 1
614 636
615 637 [logger_beaker]
616 638 level = DEBUG
617 639 handlers =
618 640 qualname = beaker.container
619 641 propagate = 1
620 642
621 643 [logger_templates]
622 644 level = INFO
623 645 handlers =
624 646 qualname = pylons.templating
625 647 propagate = 1
626 648
627 649 [logger_rhodecode]
628 650 level = DEBUG
629 651 handlers =
630 652 qualname = rhodecode
631 653 propagate = 1
632 654
633 655 [logger_sqlalchemy]
634 656 level = INFO
635 657 handlers = console_sql
636 658 qualname = sqlalchemy.engine
637 659 propagate = 0
638 660
661 [logger_ssh_wrapper]
662 level = DEBUG
663 handlers =
664 qualname = ssh_wrapper
665 propagate = 1
666
667
639 668 ##############
640 669 ## HANDLERS ##
641 670 ##############
642 671
643 672 [handler_console]
644 673 class = StreamHandler
645 674 args = (sys.stderr, )
646 675 level = INFO
647 676 formatter = generic
648 677
649 678 [handler_console_sql]
650 679 class = StreamHandler
651 680 args = (sys.stderr, )
652 681 level = WARN
653 682 formatter = generic
654 683
655 684 ################
656 685 ## FORMATTERS ##
657 686 ################
658 687
659 688 [formatter_generic]
660 689 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
661 690 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
662 691 datefmt = %Y-%m-%d %H:%M:%S
663 692
664 693 [formatter_color_formatter]
665 694 class = rhodecode.lib.logging_formatter.ColorFormatter
666 695 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
667 696 datefmt = %Y-%m-%d %H:%M:%S
668 697
669 698 [formatter_color_formatter_sql]
670 699 class = rhodecode.lib.logging_formatter.ColorFormatterSql
671 700 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
672 701 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,321 +1,351 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-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 inspect
22 22 import logging
23 23 import itertools
24 24
25 25 from rhodecode.api import (
26 26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods)
27 27
28 28 from rhodecode.api.utils import (
29 29 Optional, OAttr, has_superadmin_permission, get_user_or_error)
30 30 from rhodecode.lib.utils import repo2db_mapper
31 31 from rhodecode.lib import system_info
32 32 from rhodecode.lib import user_sessions
33 33 from rhodecode.lib.utils2 import safe_int
34 34 from rhodecode.model.db import UserIpMap
35 35 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.settings import VcsSettingsModel
36 37
37 38 log = logging.getLogger(__name__)
38 39
39 40
40 41 @jsonrpc_method()
41 42 def get_server_info(request, apiuser):
42 43 """
43 44 Returns the |RCE| server information.
44 45
45 46 This includes the running version of |RCE| and all installed
46 47 packages. This command takes the following options:
47 48
48 49 :param apiuser: This is filled automatically from the |authtoken|.
49 50 :type apiuser: AuthUser
50 51
51 52 Example output:
52 53
53 54 .. code-block:: bash
54 55
55 56 id : <id_given_in_input>
56 57 result : {
57 58 'modules': [<module name>,...]
58 59 'py_version': <python version>,
59 60 'platform': <platform type>,
60 61 'rhodecode_version': <rhodecode version>
61 62 }
62 63 error : null
63 64 """
64 65
65 66 if not has_superadmin_permission(apiuser):
66 67 raise JSONRPCForbidden()
67 68
68 69 server_info = ScmModel().get_server_info(request.environ)
69 70 # rhodecode-index requires those
70 71
71 72 server_info['index_storage'] = server_info['search']['value']['location']
72 73 server_info['storage'] = server_info['storage']['value']['path']
73 74
74 75 return server_info
75 76
76 77
77 78 @jsonrpc_method()
79 def get_repo_store(request, apiuser):
80 """
81 Returns the |RCE| repository storage information.
82
83 :param apiuser: This is filled automatically from the |authtoken|.
84 :type apiuser: AuthUser
85
86 Example output:
87
88 .. code-block:: bash
89
90 id : <id_given_in_input>
91 result : {
92 'modules': [<module name>,...]
93 'py_version': <python version>,
94 'platform': <platform type>,
95 'rhodecode_version': <rhodecode version>
96 }
97 error : null
98 """
99
100 if not has_superadmin_permission(apiuser):
101 raise JSONRPCForbidden()
102
103 path = VcsSettingsModel().get_repos_location()
104 return {"path": path}
105
106
107 @jsonrpc_method()
78 108 def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))):
79 109 """
80 110 Displays the IP Address as seen from the |RCE| server.
81 111
82 112 * This command displays the IP Address, as well as all the defined IP
83 113 addresses for the specified user. If the ``userid`` is not set, the
84 114 data returned is for the user calling the method.
85 115
86 116 This command can only be run using an |authtoken| with admin rights to
87 117 the specified repository.
88 118
89 119 This command takes the following options:
90 120
91 121 :param apiuser: This is filled automatically from |authtoken|.
92 122 :type apiuser: AuthUser
93 123 :param userid: Sets the userid for which associated IP Address data
94 124 is returned.
95 125 :type userid: Optional(str or int)
96 126
97 127 Example output:
98 128
99 129 .. code-block:: bash
100 130
101 131 id : <id_given_in_input>
102 132 result : {
103 133 "server_ip_addr": "<ip_from_clien>",
104 134 "user_ips": [
105 135 {
106 136 "ip_addr": "<ip_with_mask>",
107 137 "ip_range": ["<start_ip>", "<end_ip>"],
108 138 },
109 139 ...
110 140 ]
111 141 }
112 142
113 143 """
114 144 if not has_superadmin_permission(apiuser):
115 145 raise JSONRPCForbidden()
116 146
117 147 userid = Optional.extract(userid, evaluate_locals=locals())
118 148 userid = getattr(userid, 'user_id', userid)
119 149
120 150 user = get_user_or_error(userid)
121 151 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
122 152 return {
123 153 'server_ip_addr': request.rpc_ip_addr,
124 154 'user_ips': ips
125 155 }
126 156
127 157
128 158 @jsonrpc_method()
129 159 def rescan_repos(request, apiuser, remove_obsolete=Optional(False)):
130 160 """
131 161 Triggers a rescan of the specified repositories.
132 162
133 163 * If the ``remove_obsolete`` option is set, it also deletes repositories
134 164 that are found in the database but not on the file system, so called
135 165 "clean zombies".
136 166
137 167 This command can only be run using an |authtoken| with admin rights to
138 168 the specified repository.
139 169
140 170 This command takes the following options:
141 171
142 172 :param apiuser: This is filled automatically from the |authtoken|.
143 173 :type apiuser: AuthUser
144 174 :param remove_obsolete: Deletes repositories from the database that
145 175 are not found on the filesystem.
146 176 :type remove_obsolete: Optional(``True`` | ``False``)
147 177
148 178 Example output:
149 179
150 180 .. code-block:: bash
151 181
152 182 id : <id_given_in_input>
153 183 result : {
154 184 'added': [<added repository name>,...]
155 185 'removed': [<removed repository name>,...]
156 186 }
157 187 error : null
158 188
159 189 Example error output:
160 190
161 191 .. code-block:: bash
162 192
163 193 id : <id_given_in_input>
164 194 result : null
165 195 error : {
166 196 'Error occurred during rescan repositories action'
167 197 }
168 198
169 199 """
170 200 if not has_superadmin_permission(apiuser):
171 201 raise JSONRPCForbidden()
172 202
173 203 try:
174 204 rm_obsolete = Optional.extract(remove_obsolete)
175 205 added, removed = repo2db_mapper(ScmModel().repo_scan(),
176 206 remove_obsolete=rm_obsolete)
177 207 return {'added': added, 'removed': removed}
178 208 except Exception:
179 209 log.exception('Failed to run repo rescann')
180 210 raise JSONRPCError(
181 211 'Error occurred during rescan repositories action'
182 212 )
183 213
184 214
185 215 @jsonrpc_method()
186 216 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
187 217 """
188 218 Triggers a session cleanup action.
189 219
190 220 If the ``older_then`` option is set, only sessions that hasn't been
191 221 accessed in the given number of days will be removed.
192 222
193 223 This command can only be run using an |authtoken| with admin rights to
194 224 the specified repository.
195 225
196 226 This command takes the following options:
197 227
198 228 :param apiuser: This is filled automatically from the |authtoken|.
199 229 :type apiuser: AuthUser
200 230 :param older_then: Deletes session that hasn't been accessed
201 231 in given number of days.
202 232 :type older_then: Optional(int)
203 233
204 234 Example output:
205 235
206 236 .. code-block:: bash
207 237
208 238 id : <id_given_in_input>
209 239 result: {
210 240 "backend": "<type of backend>",
211 241 "sessions_removed": <number_of_removed_sessions>
212 242 }
213 243 error : null
214 244
215 245 Example error output:
216 246
217 247 .. code-block:: bash
218 248
219 249 id : <id_given_in_input>
220 250 result : null
221 251 error : {
222 252 'Error occurred during session cleanup'
223 253 }
224 254
225 255 """
226 256 if not has_superadmin_permission(apiuser):
227 257 raise JSONRPCForbidden()
228 258
229 259 older_then = safe_int(Optional.extract(older_then)) or 60
230 260 older_than_seconds = 60 * 60 * 24 * older_then
231 261
232 262 config = system_info.rhodecode_config().get_value()['value']['config']
233 263 session_model = user_sessions.get_session_handler(
234 264 config.get('beaker.session.type', 'memory'))(config)
235 265
236 266 backend = session_model.SESSION_TYPE
237 267 try:
238 268 cleaned = session_model.clean_sessions(
239 269 older_than_seconds=older_than_seconds)
240 270 return {'sessions_removed': cleaned, 'backend': backend}
241 271 except user_sessions.CleanupCommand as msg:
242 272 return {'cleanup_command': msg.message, 'backend': backend}
243 273 except Exception as e:
244 274 log.exception('Failed session cleanup')
245 275 raise JSONRPCError(
246 276 'Error occurred during session cleanup'
247 277 )
248 278
249 279
250 280 @jsonrpc_method()
251 281 def get_method(request, apiuser, pattern=Optional('*')):
252 282 """
253 283 Returns list of all available API methods. By default match pattern
254 284 os "*" but any other pattern can be specified. eg *comment* will return
255 285 all methods with comment inside them. If just single method is matched
256 286 returned data will also include method specification
257 287
258 288 This command can only be run using an |authtoken| with admin rights to
259 289 the specified repository.
260 290
261 291 This command takes the following options:
262 292
263 293 :param apiuser: This is filled automatically from the |authtoken|.
264 294 :type apiuser: AuthUser
265 295 :param pattern: pattern to match method names against
266 296 :type older_then: Optional("*")
267 297
268 298 Example output:
269 299
270 300 .. code-block:: bash
271 301
272 302 id : <id_given_in_input>
273 303 "result": [
274 304 "changeset_comment",
275 305 "comment_pull_request",
276 306 "comment_commit"
277 307 ]
278 308 error : null
279 309
280 310 .. code-block:: bash
281 311
282 312 id : <id_given_in_input>
283 313 "result": [
284 314 "comment_commit",
285 315 {
286 316 "apiuser": "<RequiredType>",
287 317 "comment_type": "<Optional:u'note'>",
288 318 "commit_id": "<RequiredType>",
289 319 "message": "<RequiredType>",
290 320 "repoid": "<RequiredType>",
291 321 "request": "<RequiredType>",
292 322 "resolves_comment_id": "<Optional:None>",
293 323 "status": "<Optional:None>",
294 324 "userid": "<Optional:<OptionalAttr:apiuser>>"
295 325 }
296 326 ]
297 327 error : null
298 328 """
299 329 if not has_superadmin_permission(apiuser):
300 330 raise JSONRPCForbidden()
301 331
302 332 pattern = Optional.extract(pattern)
303 333
304 334 matches = find_methods(request.registry.jsonrpc_methods, pattern)
305 335
306 336 args_desc = []
307 337 if len(matches) == 1:
308 338 func = matches[matches.keys()[0]]
309 339
310 340 argspec = inspect.getargspec(func)
311 341 arglist = argspec[0]
312 342 defaults = map(repr, argspec[3] or [])
313 343
314 344 default_empty = '<RequiredType>'
315 345
316 346 # kw arguments required by this method
317 347 func_kwargs = dict(itertools.izip_longest(
318 348 reversed(arglist), reversed(defaults), fillvalue=default_empty))
319 349 args_desc.append(func_kwargs)
320 350
321 351 return matches.keys() + args_desc
@@ -1,54 +1,70 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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
23 23 from . import config_keys
24 24 from .events import SshKeyFileChangeEvent
25 25 from .subscribers import generate_ssh_authorized_keys_file_subscriber
26 26
27 27 from rhodecode.config.middleware import _bool_setting, _string_setting
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 32 def _sanitize_settings_and_apply_defaults(settings):
33 33 """
34 34 Set defaults, convert to python types and validate settings.
35 35 """
36 36 _bool_setting(settings, config_keys.generate_authorized_keyfile, 'false')
37 37 _bool_setting(settings, config_keys.wrapper_allow_shell, 'false')
38 _bool_setting(settings, config_keys.enable_debug_logging, 'false')
38 39
39 _string_setting(settings, config_keys.authorized_keys_file_path, '',
40 _string_setting(settings, config_keys.authorized_keys_file_path,
41 '~/.ssh/authorized_keys_rhodecode',
40 42 lower=False)
41 43 _string_setting(settings, config_keys.wrapper_cmd, '',
42 44 lower=False)
43 45 _string_setting(settings, config_keys.authorized_keys_line_ssh_opts, '',
44 46 lower=False)
45 47
48 _string_setting(settings, config_keys.ssh_api_key, '',
49 lower=False)
50 _string_setting(settings, config_keys.ssh_api_host, '',
51 lower=False)
52 _string_setting(settings, config_keys.ssh_hg_bin,
53 '~/.rccontrol/vcsserver-1/profile/bin/hg',
54 lower=False)
55 _string_setting(settings, config_keys.ssh_git_bin,
56 '~/.rccontrol/vcsserver-1/profile/bin/git',
57 lower=False)
58 _string_setting(settings, config_keys.ssh_svn_bin,
59 '~/.rccontrol/vcsserver-1/profile/bin/svnserve',
60 lower=False)
61
46 62
47 63 def includeme(config):
48 64 settings = config.registry.settings
49 65 _sanitize_settings_and_apply_defaults(settings)
50 66
51 67 # if we have enable generation of file, subscribe to event
52 68 if settings[config_keys.generate_authorized_keyfile]:
53 69 config.add_subscriber(
54 70 generate_ssh_authorized_keys_file_subscriber, SshKeyFileChangeEvent)
@@ -1,28 +1,36 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 # Definition of setting keys used to configure this module. Defined here to
23 23 # avoid repetition of keys throughout the module.
24 24 generate_authorized_keyfile = 'ssh.generate_authorized_keyfile'
25 25 authorized_keys_file_path = 'ssh.authorized_keys_file_path'
26 26 authorized_keys_line_ssh_opts = 'ssh.authorized_keys_ssh_opts'
27 27 wrapper_cmd = 'ssh.wrapper_cmd'
28 28 wrapper_allow_shell = 'ssh.wrapper_cmd_allow_shell'
29 enable_debug_logging = 'ssh.enable_debug_logging'
30
31 ssh_api_key = 'ssh.api_key'
32 ssh_api_host = 'ssh.api_host'
33
34 ssh_hg_bin = 'ssh.executable.hg'
35 ssh_git_bin = 'ssh.executable.git'
36 ssh_svn_bin = 'ssh.executable.svn'
@@ -1,68 +1,73 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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
23 23 import os
24 24 import pytest
25 25 import mock
26 26
27 27 from rhodecode.apps.ssh_support import utils
28 28 from rhodecode.lib.utils2 import AttributeDict
29 29
30 30
31 31 class TestSshKeyFileGeneration(object):
32 32 @pytest.mark.parametrize('ssh_wrapper_cmd', ['/tmp/sshwrapper.py'])
33 33 @pytest.mark.parametrize('allow_shell', [True, False])
34 @pytest.mark.parametrize('debug', [True, False])
34 35 @pytest.mark.parametrize('ssh_opts', [None, 'mycustom,option'])
35 def test_write_keyfile(self, tmpdir, ssh_wrapper_cmd, allow_shell, ssh_opts):
36 def test_write_keyfile(self, tmpdir, ssh_wrapper_cmd, allow_shell, debug, ssh_opts):
36 37
37 38 authorized_keys_file_path = os.path.join(str(tmpdir), 'authorized_keys')
38 39
39 40 def keys():
40 41 return [
41 42 AttributeDict({'user': AttributeDict(username='admin'),
42 43 'ssh_key_data': 'ssh-rsa ADMIN_KEY'}),
43 44 AttributeDict({'user': AttributeDict(username='user'),
44 45 'ssh_key_data': 'ssh-rsa USER_KEY'}),
45 46 ]
46
47 47 with mock.patch('rhodecode.apps.ssh_support.utils.get_all_active_keys',
48 48 return_value=keys()):
49 with mock.patch.dict('rhodecode.CONFIG', {'__file__': '/tmp/file.ini'}):
49 50 utils._generate_ssh_authorized_keys_file(
50 51 authorized_keys_file_path, ssh_wrapper_cmd,
51 allow_shell, ssh_opts
52 allow_shell, ssh_opts, debug
52 53 )
53 54
54 55 assert os.path.isfile(authorized_keys_file_path)
55 56 with open(authorized_keys_file_path) as f:
56 57 content = f.read()
57 58
58 59 assert 'command="/tmp/sshwrapper.py' in content
59 60 assert 'This file is managed by RhodeCode, ' \
60 61 'please do not edit it manually.' in content
61 62
62 63 if allow_shell:
63 assert '--shell --user' in content
64 else:
64 assert '--shell' in content
65
66 if debug:
67 assert '--debug' in content
68
65 69 assert '--user' in content
70 assert '--user-id' in content
66 71
67 72 if ssh_opts:
68 73 assert ssh_opts in content
@@ -1,107 +1,119 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 os
22 22 import stat
23 23 import logging
24 24 import tempfile
25 25 import datetime
26 26
27 27 from . import config_keys
28 28 from rhodecode.model.db import true, joinedload, User, UserSshKeys
29 29
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33 HEADER = \
34 34 "# This file is managed by RhodeCode, please do not edit it manually. # \n" \
35 35 "# Current entries: {}, create date: UTC:{}.\n"
36 36
37 37 # Default SSH options for authorized_keys file, can be override via .ini
38 38 SSH_OPTS = 'no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding'
39 39
40 40
41 41 def get_all_active_keys():
42 42 result = UserSshKeys.query() \
43 43 .options(joinedload(UserSshKeys.user)) \
44 44 .filter(UserSshKeys.user != User.get_default_user()) \
45 45 .filter(User.active == true()) \
46 46 .all()
47 47 return result
48 48
49 49
50 50 def _generate_ssh_authorized_keys_file(
51 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts):
51 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts, debug):
52
53 import rhodecode
52 54 all_active_keys = get_all_active_keys()
53 55
54 56 if allow_shell:
55 57 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --shell'
58 if debug:
59 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --debug'
56 60
57 61 if not os.path.isfile(authorized_keys_file_path):
58 62 with open(authorized_keys_file_path, 'w'):
59 63 pass
60 64
61 65 if not os.access(authorized_keys_file_path, os.R_OK):
62 66 raise OSError('Access to file {} is without read access'.format(
63 67 authorized_keys_file_path))
64 68
65 line_tmpl = '{ssh_opts},command="{wrapper_command} --user {user}" {key}\n'
69 line_tmpl = '{ssh_opts},command="{wrapper_command} {ini_path} --user-id={user_id} --user={user}" {key}\n'
66 70
67 71 fd, tmp_authorized_keys = tempfile.mkstemp(
68 72 '.authorized_keys_write',
69 73 dir=os.path.dirname(authorized_keys_file_path))
70 74
71 75 now = datetime.datetime.utcnow().isoformat()
72 76 keys_file = os.fdopen(fd, 'wb')
73 77 keys_file.write(HEADER.format(len(all_active_keys), now))
78 ini_path = rhodecode.CONFIG['__file__']
74 79
75 80 for user_key in all_active_keys:
76 81 username = user_key.user.username
82 user_id = user_key.user.user_id
83
77 84 keys_file.write(
78 85 line_tmpl.format(
79 86 ssh_opts=ssh_opts or SSH_OPTS,
80 87 wrapper_command=ssh_wrapper_cmd,
88 ini_path=ini_path,
89 user_id=user_id,
81 90 user=username, key=user_key.ssh_key_data))
82 91 log.debug('addkey: Key added for user: `%s`', username)
83 92 keys_file.close()
84 93
85 94 # Explicitly setting read-only permissions to authorized_keys
86 95 os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR)
87 96 # Rename is atomic operation
88 97 os.rename(tmp_authorized_keys, authorized_keys_file_path)
89 98
90 99
91 100 def generate_ssh_authorized_keys_file(registry):
92 101 log.info('Generating new authorized key file')
93 102
94 103 authorized_keys_file_path = registry.settings.get(
95 104 config_keys.authorized_keys_file_path)
96 105
97 106 ssh_wrapper_cmd = registry.settings.get(
98 107 config_keys.wrapper_cmd)
99 108 allow_shell = registry.settings.get(
100 109 config_keys.wrapper_allow_shell)
101 110 ssh_opts = registry.settings.get(
102 111 config_keys.authorized_keys_line_ssh_opts)
112 debug = registry.settings.get(
113 config_keys.enable_debug_logging)
103 114
104 115 _generate_ssh_authorized_keys_file(
105 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts)
116 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts,
117 debug)
106 118
107 119 return 0
@@ -1,722 +1,776 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 ## change this to unique ID for security
211 211 app_instance_uuid = rc-production
212 212
213 213 ## cut off limit for large diffs (size in bytes)
214 214 cut_off_limit_diff = 1024000
215 215 cut_off_limit_file = 256000
216 216
217 217 ## use cache version of scm repo everywhere
218 218 vcs_full_cache = false
219 219
220 220 ## force https in RhodeCode, fixes https redirects, assumes it's always https
221 221 ## Normally this is controlled by proper http flags sent from http server
222 222 force_https = false
223 223
224 224 ## use Strict-Transport-Security headers
225 225 use_htsts = false
226 226
227 227 ## number of commits stats will parse on each iteration
228 228 commit_parse_limit = 25
229 229
230 230 ## git rev filter option, --all is the default filter, if you need to
231 231 ## hide all refs in changelog switch this to --branches --tags
232 232 git_rev_filter = --all
233 233
234 234 # Set to true if your repos are exposed using the dumb protocol
235 235 git_update_server_info = false
236 236
237 237 ## RSS/ATOM feed options
238 238 rss_cut_off_limit = 256000
239 239 rss_items_per_page = 10
240 240 rss_include_diff = false
241 241
242 242 ## gist URL alias, used to create nicer urls for gist. This should be an
243 243 ## url that does rewrites to _admin/gists/{gistid}.
244 244 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
245 245 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
246 246 gist_alias_url =
247 247
248 248 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
249 249 ## used for access.
250 250 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
251 251 ## came from the the logged in user who own this authentication token.
252 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
253 ## authentication token. Such view would be only accessible when used together
254 ## with this authentication token
252 255 ##
253 ## list of all views can be found under `_admin/permissions/auth_token_access`
256 ## list of all views can be found under `/_admin/permissions/auth_token_access`
254 257 ## The list should be "," separated and on a single line.
255 258 ##
256 259 ## Most common views to enable:
257 260 # RepoCommitsView:repo_commit_download
258 261 # RepoCommitsView:repo_commit_patch
259 262 # RepoCommitsView:repo_commit_raw
263 # RepoCommitsView:repo_commit_raw@TOKEN
260 264 # RepoFilesView:repo_files_diff
261 265 # RepoFilesView:repo_archivefile
262 266 # RepoFilesView:repo_file_raw
263 267 # GistView:*
264 268 api_access_controllers_whitelist =
265 269
266 270 ## default encoding used to convert from and to unicode
267 271 ## can be also a comma separated list of encoding in case of mixed encodings
268 272 default_encoding = UTF-8
269 273
270 274 ## instance-id prefix
271 275 ## a prefix key for this instance used for cache invalidation when running
272 276 ## multiple instances of rhodecode, make sure it's globally unique for
273 277 ## all running rhodecode instances. Leave empty if you don't use it
274 278 instance_id =
275 279
276 280 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
277 281 ## of an authentication plugin also if it is disabled by it's settings.
278 282 ## This could be useful if you are unable to log in to the system due to broken
279 283 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
280 284 ## module to log in again and fix the settings.
281 285 ##
282 286 ## Available builtin plugin IDs (hash is part of the ID):
283 287 ## egg:rhodecode-enterprise-ce#rhodecode
284 288 ## egg:rhodecode-enterprise-ce#pam
285 289 ## egg:rhodecode-enterprise-ce#ldap
286 290 ## egg:rhodecode-enterprise-ce#jasig_cas
287 291 ## egg:rhodecode-enterprise-ce#headers
288 292 ## egg:rhodecode-enterprise-ce#crowd
289 293 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
290 294
291 295 ## alternative return HTTP header for failed authentication. Default HTTP
292 296 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
293 297 ## handling that causing a series of failed authentication calls.
294 298 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
295 299 ## This will be served instead of default 401 on bad authnetication
296 300 auth_ret_code =
297 301
298 302 ## use special detection method when serving auth_ret_code, instead of serving
299 303 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
300 304 ## and then serve auth_ret_code to clients
301 305 auth_ret_code_detection = false
302 306
303 307 ## locking return code. When repository is locked return this HTTP code. 2XX
304 308 ## codes don't break the transactions while 4XX codes do
305 309 lock_ret_code = 423
306 310
307 311 ## allows to change the repository location in settings page
308 312 allow_repo_location_change = true
309 313
310 314 ## allows to setup custom hooks in settings page
311 315 allow_custom_hooks_settings = true
312 316
313 317 ## generated license token, goto license page in RhodeCode settings to obtain
314 318 ## new token
315 319 license_token = abra-cada-bra1-rce3
316 320
317 321 ## supervisor connection uri, for managing supervisor and logs.
318 322 supervisor.uri =
319 323 ## supervisord group name/id we only want this RC instance to handle
320 324 supervisor.group_id = dev
321 325
322 326 ## Display extended labs settings
323 327 labs_settings_active = true
324 328
325 329 ####################################
326 330 ### CELERY CONFIG ####
327 331 ####################################
328 332 use_celery = false
329 333 broker.host = localhost
330 334 broker.vhost = rabbitmqhost
331 335 broker.port = 5672
332 336 broker.user = rabbitmq
333 337 broker.password = qweqwe
334 338
335 339 celery.imports = rhodecode.lib.celerylib.tasks
336 340
337 341 celery.result.backend = amqp
338 342 celery.result.dburi = amqp://
339 343 celery.result.serialier = json
340 344
341 345 #celery.send.task.error.emails = true
342 346 #celery.amqp.task.result.expires = 18000
343 347
344 348 celeryd.concurrency = 2
345 349 #celeryd.log.file = celeryd.log
346 350 celeryd.log.level = debug
347 351 celeryd.max.tasks.per.child = 1
348 352
349 353 ## tasks will never be sent to the queue, but executed locally instead.
350 354 celery.always.eager = false
351 355
352 356 ####################################
353 357 ### BEAKER CACHE ####
354 358 ####################################
355 359 # default cache dir for templates. Putting this into a ramdisk
356 360 ## can boost performance, eg. %(here)s/data_ramdisk
357 361 cache_dir = %(here)s/data
358 362
359 363 ## locking and default file storage for Beaker. Putting this into a ramdisk
360 364 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
361 365 beaker.cache.data_dir = %(here)s/rc/data/cache/beaker_data
362 366 beaker.cache.lock_dir = %(here)s/rc/data/cache/beaker_lock
363 367
364 368 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
365 369
366 370 beaker.cache.super_short_term.type = memory
367 371 beaker.cache.super_short_term.expire = 1
368 372 beaker.cache.super_short_term.key_length = 256
369 373
370 374 beaker.cache.short_term.type = memory
371 375 beaker.cache.short_term.expire = 60
372 376 beaker.cache.short_term.key_length = 256
373 377
374 378 beaker.cache.long_term.type = memory
375 379 beaker.cache.long_term.expire = 36000
376 380 beaker.cache.long_term.key_length = 256
377 381
378 382 beaker.cache.sql_cache_short.type = memory
379 383 beaker.cache.sql_cache_short.expire = 1
380 384 beaker.cache.sql_cache_short.key_length = 256
381 385
382 386 ## default is memory cache, configure only if required
383 387 ## using multi-node or multi-worker setup
384 388 #beaker.cache.auth_plugins.type = ext:database
385 389 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
386 390 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
387 391 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
388 392 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
389 393 #beaker.cache.auth_plugins.sa.pool_size = 10
390 394 #beaker.cache.auth_plugins.sa.max_overflow = 0
391 395
392 396 beaker.cache.repo_cache_long.type = memorylru_base
393 397 beaker.cache.repo_cache_long.max_items = 4096
394 398 beaker.cache.repo_cache_long.expire = 2592000
395 399
396 400 ## default is memorylru_base cache, configure only if required
397 401 ## using multi-node or multi-worker setup
398 402 #beaker.cache.repo_cache_long.type = ext:memcached
399 403 #beaker.cache.repo_cache_long.url = localhost:11211
400 404 #beaker.cache.repo_cache_long.expire = 1209600
401 405 #beaker.cache.repo_cache_long.key_length = 256
402 406
403 407 ####################################
404 408 ### BEAKER SESSION ####
405 409 ####################################
406 410
407 411 ## .session.type is type of storage options for the session, current allowed
408 412 ## types are file, ext:memcached, ext:database, and memory (default).
409 413 beaker.session.type = file
410 414 beaker.session.data_dir = %(here)s/rc/data/sessions/data
411 415
412 416 ## db based session, fast, and allows easy management over logged in users
413 417 #beaker.session.type = ext:database
414 418 #beaker.session.table_name = db_session
415 419 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
416 420 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
417 421 #beaker.session.sa.pool_recycle = 3600
418 422 #beaker.session.sa.echo = false
419 423
420 424 beaker.session.key = rhodecode
421 425 beaker.session.secret = test-rc-uytcxaz
422 426 beaker.session.lock_dir = %(here)s/rc/data/sessions/lock
423 427
424 428 ## Secure encrypted cookie. Requires AES and AES python libraries
425 429 ## you must disable beaker.session.secret to use this
426 430 #beaker.session.encrypt_key = key_for_encryption
427 431 #beaker.session.validate_key = validation_key
428 432
429 433 ## sets session as invalid(also logging out user) if it haven not been
430 434 ## accessed for given amount of time in seconds
431 435 beaker.session.timeout = 2592000
432 436 beaker.session.httponly = true
433 437 ## Path to use for the cookie. Set to prefix if you use prefix middleware
434 438 #beaker.session.cookie_path = /custom_prefix
435 439
436 440 ## uncomment for https secure cookie
437 441 beaker.session.secure = false
438 442
439 443 ## auto save the session to not to use .save()
440 444 beaker.session.auto = false
441 445
442 446 ## default cookie expiration time in seconds, set to `true` to set expire
443 447 ## at browser close
444 448 #beaker.session.cookie_expires = 3600
445 449
446 450 ###################################
447 451 ## SEARCH INDEXING CONFIGURATION ##
448 452 ###################################
449 453 ## Full text search indexer is available in rhodecode-tools under
450 454 ## `rhodecode-tools index` command
451 455
452 456 ## WHOOSH Backend, doesn't require additional services to run
453 457 ## it works good with few dozen repos
454 458 search.module = rhodecode.lib.index.whoosh
455 459 search.location = %(here)s/data/index
456 460
457 461 ########################################
458 462 ### CHANNELSTREAM CONFIG ####
459 463 ########################################
460 464 ## channelstream enables persistent connections and live notification
461 465 ## in the system. It's also used by the chat system
462 466
463 467 channelstream.enabled = false
464 468
465 469 ## server address for channelstream server on the backend
466 470 channelstream.server = 127.0.0.1:9800
467 471 ## location of the channelstream server from outside world
468 472 ## use ws:// for http or wss:// for https. This address needs to be handled
469 473 ## by external HTTP server such as Nginx or Apache
470 474 ## see nginx/apache configuration examples in our docs
471 475 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
472 476 channelstream.secret = secret
473 477 channelstream.history.location = %(here)s/channelstream_history
474 478
475 479 ## Internal application path that Javascript uses to connect into.
476 480 ## If you use proxy-prefix the prefix should be added before /_channelstream
477 481 channelstream.proxy_path = /_channelstream
478 482
479 483
480 484 ###################################
481 485 ## APPENLIGHT CONFIG ##
482 486 ###################################
483 487
484 488 ## Appenlight is tailored to work with RhodeCode, see
485 489 ## http://appenlight.com for details how to obtain an account
486 490
487 491 ## appenlight integration enabled
488 492 appenlight = false
489 493
490 494 appenlight.server_url = https://api.appenlight.com
491 495 appenlight.api_key = YOUR_API_KEY
492 496 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
493 497
494 498 # used for JS client
495 499 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
496 500
497 501 ## TWEAK AMOUNT OF INFO SENT HERE
498 502
499 503 ## enables 404 error logging (default False)
500 504 appenlight.report_404 = false
501 505
502 506 ## time in seconds after request is considered being slow (default 1)
503 507 appenlight.slow_request_time = 1
504 508
505 509 ## record slow requests in application
506 510 ## (needs to be enabled for slow datastore recording and time tracking)
507 511 appenlight.slow_requests = true
508 512
509 513 ## enable hooking to application loggers
510 514 appenlight.logging = true
511 515
512 516 ## minimum log level for log capture
513 517 appenlight.logging.level = WARNING
514 518
515 519 ## send logs only from erroneous/slow requests
516 520 ## (saves API quota for intensive logging)
517 521 appenlight.logging_on_error = false
518 522
519 523 ## list of additonal keywords that should be grabbed from environ object
520 524 ## can be string with comma separated list of words in lowercase
521 525 ## (by default client will always send following info:
522 526 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
523 527 ## start with HTTP* this list be extended with additional keywords here
524 528 appenlight.environ_keys_whitelist =
525 529
526 530 ## list of keywords that should be blanked from request object
527 531 ## can be string with comma separated list of words in lowercase
528 532 ## (by default client will always blank keys that contain following words
529 533 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
530 534 ## this list be extended with additional keywords set here
531 535 appenlight.request_keys_blacklist =
532 536
533 537 ## list of namespaces that should be ignores when gathering log entries
534 538 ## can be string with comma separated list of namespaces
535 539 ## (by default the client ignores own entries: appenlight_client.client)
536 540 appenlight.log_namespace_blacklist =
537 541
538 542
539 543 ################################################################################
540 544 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
541 545 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
542 546 ## execute malicious code after an exception is raised. ##
543 547 ################################################################################
544 548 set debug = false
545 549
546 550
547 551 ##############
548 552 ## STYLING ##
549 553 ##############
550 554 debug_style = false
551 555
552 556 ###########################################
553 557 ### MAIN RHODECODE DATABASE CONFIG ###
554 558 ###########################################
555 559 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
556 560 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode_test
557 561 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode_test
558 562 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
559 563
560 564 # see sqlalchemy docs for other advanced settings
561 565
562 566 ## print the sql statements to output
563 567 sqlalchemy.db1.echo = false
564 568 ## recycle the connections after this amount of seconds
565 569 sqlalchemy.db1.pool_recycle = 3600
566 570 sqlalchemy.db1.convert_unicode = true
567 571
568 572 ## the number of connections to keep open inside the connection pool.
569 573 ## 0 indicates no limit
570 574 #sqlalchemy.db1.pool_size = 5
571 575
572 576 ## the number of connections to allow in connection pool "overflow", that is
573 577 ## connections that can be opened above and beyond the pool_size setting,
574 578 ## which defaults to five.
575 579 #sqlalchemy.db1.max_overflow = 10
576 580
577 581
578 582 ##################
579 583 ### VCS CONFIG ###
580 584 ##################
581 585 vcs.server.enable = true
582 586 vcs.server = localhost:9901
583 587
584 588 ## Web server connectivity protocol, responsible for web based VCS operatations
585 589 ## Available protocols are:
586 590 ## `http` - use http-rpc backend (default)
587 591 vcs.server.protocol = http
588 592
589 593 ## Push/Pull operations protocol, available options are:
590 ## `rhodecode.lib.middleware.utils.scm_app_http` - Http based, recommended
594 ## `http` - use http-rpc backend (default)
591 595 ## `vcsserver.scm_app` - internal app (EE only)
592 596 vcs.scm_app_implementation = http
593 597
594 598 ## Push/Pull operations hooks protocol, available options are:
595 599 ## `http` - use http-rpc backend (default)
596 600 vcs.hooks.protocol = http
597 601
598 602 vcs.server.log_level = debug
599 603 ## Start VCSServer with this instance as a subprocess, usefull for development
600 604 vcs.start_server = false
601 605
602 606 ## List of enabled VCS backends, available options are:
603 607 ## `hg` - mercurial
604 608 ## `git` - git
605 609 ## `svn` - subversion
606 610 vcs.backends = hg, git, svn
607 611
608 612 vcs.connection_timeout = 3600
609 613 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
610 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
614 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
611 615 #vcs.svn.compatible_version = pre-1.8-compatible
612 616
613 617
614 618 ############################################################
615 619 ### Subversion proxy support (mod_dav_svn) ###
616 620 ### Maps RhodeCode repo groups into SVN paths for Apache ###
617 621 ############################################################
618 622 ## Enable or disable the config file generation.
619 623 svn.proxy.generate_config = false
620 624 ## Generate config file with `SVNListParentPath` set to `On`.
621 625 svn.proxy.list_parent_path = true
622 626 ## Set location and file name of generated config file.
623 627 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
624 628 ## Used as a prefix to the `Location` block in the generated config file.
625 629 ## In most cases it should be set to `/`.
626 630 svn.proxy.location_root = /
627 631 ## Command to reload the mod dav svn configuration on change.
628 632 ## Example: `/etc/init.d/apache2 reload`
629 633 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
630 634 ## If the timeout expires before the reload command finishes, the command will
631 635 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
632 636 #svn.proxy.reload_timeout = 10
633 637
638 ############################################################
639 ### SSH Support Settings ###
640 ############################################################
641
642 ## Defines if the authorized_keys file should be written on any change of
643 ## user ssh keys
644 ssh.generate_authorized_keyfile = false
645
646 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
647 # ssh.authorized_keys_ssh_opts =
648
649 ## File to generate the authorized keys together with options
650 ## It is possible to have multiple key files specified in `sshd_config` e.g.
651 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
652 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
653
654 ## Command to execute the SSH wrapper. The binary is available in the
655 ## rhodecode installation directory.
656 ## e.g ~/.rccontrol/community-1/profile/bin/rcssh-wrapper
657 ssh.wrapper_cmd = ~/.rccontrol/community-1/rcssh-wrapper
658
659 ## Allow shell when executing the ssh-wrapper command
660 ssh.wrapper_cmd_allow_shell = false
661
662 ## Enables logging, and detailed output send back to the client. Usefull for
663 ## debugging, shouldn't be used in production.
664 ssh.enable_debug_logging = false
665
666 ## API KEY for user who has access to fetch other user permission information
667 ## most likely an super-admin account with some IP restrictions.
668 ssh.api_key =
669
670 ## API Host, the server address of RhodeCode instance that the api_key will
671 ## access
672 ssh.api_host = http://localhost
673
674 ## Paths to binary executrables, by default they are the names, but we can
675 ## override them if we want to use a custom one
676 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
677 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
678 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
679
680
634 681 ## Dummy marker to add new entries after.
635 682 ## Add any custom entries below. Please don't remove.
636 683 custom.conf = 1
637 684
638 685
639 686 ################################
640 687 ### LOGGING CONFIGURATION ####
641 688 ################################
642 689 [loggers]
643 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
690 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, ssh_wrapper
644 691
645 692 [handlers]
646 693 keys = console, console_sql
647 694
648 695 [formatters]
649 696 keys = generic, color_formatter, color_formatter_sql
650 697
651 698 #############
652 699 ## LOGGERS ##
653 700 #############
654 701 [logger_root]
655 702 level = NOTSET
656 703 handlers = console
657 704
658 705 [logger_routes]
659 706 level = DEBUG
660 707 handlers =
661 708 qualname = routes.middleware
662 709 ## "level = DEBUG" logs the route matched and routing variables.
663 710 propagate = 1
664 711
665 712 [logger_beaker]
666 713 level = DEBUG
667 714 handlers =
668 715 qualname = beaker.container
669 716 propagate = 1
670 717
671 718 [logger_templates]
672 719 level = INFO
673 720 handlers =
674 721 qualname = pylons.templating
675 722 propagate = 1
676 723
677 724 [logger_rhodecode]
678 725 level = DEBUG
679 726 handlers =
680 727 qualname = rhodecode
681 728 propagate = 1
682 729
683 730 [logger_sqlalchemy]
684 731 level = ERROR
685 732 handlers = console_sql
686 733 qualname = sqlalchemy.engine
687 734 propagate = 0
688 735
736 [logger_ssh_wrapper]
737 level = DEBUG
738 handlers =
739 qualname = ssh_wrapper
740 propagate = 1
741
742
689 743 ##############
690 744 ## HANDLERS ##
691 745 ##############
692 746
693 747 [handler_console]
694 748 class = StreamHandler
695 749 args = (sys.stderr,)
696 750 level = DEBUG
697 751 formatter = generic
698 752
699 753 [handler_console_sql]
700 754 class = StreamHandler
701 755 args = (sys.stderr,)
702 756 level = WARN
703 757 formatter = generic
704 758
705 759 ################
706 760 ## FORMATTERS ##
707 761 ################
708 762
709 763 [formatter_generic]
710 764 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
711 765 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
712 766 datefmt = %Y-%m-%d %H:%M:%S
713 767
714 768 [formatter_color_formatter]
715 769 class = rhodecode.lib.logging_formatter.ColorFormatter
716 770 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
717 771 datefmt = %Y-%m-%d %H:%M:%S
718 772
719 773 [formatter_color_formatter_sql]
720 774 class = rhodecode.lib.logging_formatter.ColorFormatterSql
721 775 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
722 776 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,260 +1,261 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 early to make sure things are patched up properly
22 22 from setuptools import setup, find_packages
23 23
24 24 import os
25 25 import sys
26 26 import pkgutil
27 27 import platform
28 28
29 29 from pip.download import PipSession
30 30 from pip.req import parse_requirements
31 31
32 32 from codecs import open
33 33
34 34
35 35 if sys.version_info < (2, 7):
36 36 raise Exception('RhodeCode requires Python 2.7 or later')
37 37
38 38 here = os.path.abspath(os.path.dirname(__file__))
39 39
40 40 # defines current platform
41 41 __platform__ = platform.system()
42 42 __license__ = 'AGPLv3, and Commercial License'
43 43 __author__ = 'RhodeCode GmbH'
44 44 __url__ = 'https://code.rhodecode.com'
45 45 is_windows = __platform__ in ('Windows',)
46 46
47 47
48 48 def _get_requirements(req_filename, exclude=None, extras=None):
49 49 extras = extras or []
50 50 exclude = exclude or []
51 51
52 52 try:
53 53 parsed = parse_requirements(
54 54 os.path.join(here, req_filename), session=PipSession())
55 55 except TypeError:
56 56 # try pip < 6.0.0, that doesn't support session
57 57 parsed = parse_requirements(os.path.join(here, req_filename))
58 58
59 59 requirements = []
60 60 for ir in parsed:
61 61 if ir.req and ir.name not in exclude:
62 62 requirements.append(str(ir.req))
63 63 return requirements + extras
64 64
65 65
66 66 # requirements extract
67 67 setup_requirements = ['PasteScript', 'pytest-runner']
68 68 install_requirements = _get_requirements(
69 69 'requirements.txt', exclude=['setuptools'])
70 70 test_requirements = _get_requirements(
71 71 'requirements_test.txt', extras=['configobj'])
72 72
73 73 install_requirements = [
74 74 'Babel',
75 75 'Beaker',
76 76 'FormEncode',
77 77 'Mako',
78 78 'Markdown',
79 79 'MarkupSafe',
80 80 'MySQL-python',
81 81 'Paste',
82 82 'PasteDeploy',
83 83 'PasteScript',
84 84 'Pygments',
85 85 'pygments-markdown-lexer',
86 86 'Pylons',
87 87 'Routes',
88 88 'SQLAlchemy',
89 89 'Tempita',
90 90 'URLObject',
91 91 'WebError',
92 92 'WebHelpers',
93 93 'WebHelpers2',
94 94 'WebOb',
95 95 'WebTest',
96 96 'Whoosh',
97 97 'alembic',
98 98 'amqplib',
99 99 'anyjson',
100 100 'appenlight-client',
101 101 'authomatic',
102 102 'cssselect',
103 103 'celery',
104 104 'channelstream',
105 105 'colander',
106 106 'decorator',
107 107 'deform',
108 108 'docutils',
109 109 'gevent',
110 110 'gunicorn',
111 111 'infrae.cache',
112 112 'ipython',
113 113 'iso8601',
114 114 'kombu',
115 115 'lxml',
116 116 'msgpack-python',
117 117 'nbconvert',
118 118 'packaging',
119 119 'psycopg2',
120 120 'py-gfm',
121 121 'pycrypto',
122 122 'pycurl',
123 123 'pyparsing',
124 124 'pyramid',
125 125 'pyramid-debugtoolbar',
126 126 'pyramid-mako',
127 127 'pyramid-beaker',
128 128 'pysqlite',
129 129 'python-dateutil',
130 130 'python-ldap',
131 131 'python-memcached',
132 132 'python-pam',
133 133 'recaptcha-client',
134 134 'repoze.lru',
135 135 'requests',
136 136 'simplejson',
137 137 'sshpubkeys',
138 138 'subprocess32',
139 139 'waitress',
140 140 'zope.cachedescriptors',
141 141 'dogpile.cache',
142 142 'dogpile.core',
143 143 'psutil',
144 144 'py-bcrypt',
145 145 ]
146 146
147 147
148 148 def get_version():
149 149 version = pkgutil.get_data('rhodecode', 'VERSION')
150 150 return version.strip()
151 151
152 152
153 153 # additional files that goes into package itself
154 154 package_data = {
155 155 '': ['*.txt', '*.rst'],
156 156 'configs': ['*.ini'],
157 157 'rhodecode': ['VERSION', 'i18n/*/LC_MESSAGES/*.mo', ],
158 158 }
159 159
160 160 description = 'Source Code Management Platform'
161 161 keywords = ' '.join([
162 162 'rhodecode', 'mercurial', 'git', 'svn',
163 163 'code review',
164 164 'repo groups', 'ldap', 'repository management', 'hgweb',
165 165 'hgwebdir', 'gitweb', 'serving hgweb',
166 166 ])
167 167
168 168
169 169 # README/DESCRIPTION generation
170 170 readme_file = 'README.rst'
171 171 changelog_file = 'CHANGES.rst'
172 172 try:
173 173 long_description = open(readme_file).read() + '\n\n' + \
174 174 open(changelog_file).read()
175 175 except IOError as err:
176 176 sys.stderr.write(
177 177 "[WARNING] Cannot find file specified as long_description (%s)\n "
178 178 "or changelog (%s) skipping that file" % (readme_file, changelog_file))
179 179 long_description = description
180 180
181 181
182 182 setup(
183 183 name='rhodecode-enterprise-ce',
184 184 version=get_version(),
185 185 description=description,
186 186 long_description=long_description,
187 187 keywords=keywords,
188 188 license=__license__,
189 189 author=__author__,
190 190 author_email='marcin@rhodecode.com',
191 191 url=__url__,
192 192 setup_requires=setup_requirements,
193 193 install_requires=install_requirements,
194 194 tests_require=test_requirements,
195 195 zip_safe=False,
196 196 packages=find_packages(exclude=["docs", "tests*"]),
197 197 package_data=package_data,
198 198 include_package_data=True,
199 199 classifiers=[
200 200 'Development Status :: 6 - Mature',
201 201 'Environment :: Web Environment',
202 202 'Intended Audience :: Developers',
203 203 'Operating System :: OS Independent',
204 204 'Topic :: Software Development :: Version Control',
205 205 'License :: OSI Approved :: Affero GNU General Public License v3 or later (AGPLv3+)',
206 206 'Programming Language :: Python :: 2.7',
207 207 ],
208 208 message_extractors={
209 209 'rhodecode': [
210 210 ('**.py', 'python', None),
211 211 ('**.js', 'javascript', None),
212 212 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
213 213 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
214 214 ('public/**', 'ignore', None),
215 215 ]
216 216 },
217 217 paster_plugins=['PasteScript', 'Pylons'],
218 218 entry_points={
219 219 'enterprise.plugins1': [
220 220 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
221 221 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
222 222 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
223 223 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
224 224 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
225 225 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
226 226 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
227 227 ],
228 228 'paste.app_factory': [
229 229 'main=rhodecode.config.middleware:make_pyramid_app',
230 230 'pylons=rhodecode.config.middleware:make_app',
231 231 ],
232 232 'paste.app_install': [
233 233 'main=pylons.util:PylonsInstaller',
234 234 'pylons=pylons.util:PylonsInstaller',
235 235 ],
236 236 'paste.global_paster_command': [
237 237 'make-config=rhodecode.lib.paster_commands.make_config:Command',
238 238 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
239 239 'ishell=rhodecode.lib.paster_commands.ishell:Command',
240 240 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
241 241 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
242 242 ],
243 243 'pyramid.pshell_runner': [
244 244 'ipython = rhodecode.lib.pyramid_shell:ipython_shell_runner',
245 245 ],
246 246 'pytest11': [
247 247 'pylons=rhodecode.tests.pylons_plugin',
248 248 'enterprise=rhodecode.tests.plugin',
249 249 ],
250 250 'console_scripts': [
251 251 'rcserver=rhodecode.rcserver:main',
252 252 'rcsetup-app=rhodecode.lib.rc_commands.setup_rc:main',
253 253 'rcupgrade-db=rhodecode.lib.rc_commands.upgrade_db:main',
254 'rcssh-wrapper=rhodecode.apps.ssh_support.lib.ssh_wrapper:main',
254 255 ],
255 256 'beaker.backends': [
256 257 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
257 258 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
258 259 ]
259 260 },
260 261 )
General Comments 0
You need to be logged in to leave comments. Login now