##// END OF EJS Templates
feat(configs): deprecared old hooks protocol and ssh wrapper....
feat(configs): deprecared old hooks protocol and ssh wrapper. New defaults are now set on v2 keys, so previous installation are automatically set to new keys. Fallback mode is still available.

File last commit:

r5326:cede61e3 default
r5496:cab50adf default
Show More
__init__.py
387 lines | 13.7 KiB | text/x-python | PythonLexer
copyrights: updated for 2023
r5088 # Copyright (C) 2016-2023 RhodeCode GmbH
ssh-support: enabled full handling of all backends via SSH....
r2187 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import os
import re
import logging
import datetime
ssh-wrapper: perf optimizations...
r4947 from sqlalchemy import Table
ssh-support: enabled full handling of all backends via SSH....
r2187
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 from rhodecode.lib.api_utils import call_service_api
ssh-wrapper: perf optimizations...
r4947 from rhodecode.lib.utils2 import AttributeDict
ssh-support: enabled full handling of all backends via SSH....
r2187
from .hg import MercurialServer
from .git import GitServer
from .svn import SubversionServer
log = logging.getLogger(__name__)
class SshWrapper(object):
ssh: use pre-compiled backends for faster matching of vcs detection.
r4702 hg_cmd_pat = re.compile(r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$')
git_cmd_pat = re.compile(r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
svn_cmd_pat = re.compile(r'^svnserve -t')
ssh-support: enabled full handling of all backends via SSH....
r2187
def __init__(self, command, connection_info, mode,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user, user_id, key_id: int, shell, ini_path: str, settings, env):
ssh-support: enabled full handling of all backends via SSH....
r2187 self.command = command
self.connection_info = connection_info
self.mode = mode
ssh-wrapper: perf optimizations...
r4947 self.username = user
ssh-support: enabled full handling of all backends via SSH....
r2187 self.user_id = user_id
self.key_id = key_id
self.shell = shell
self.ini_path = ini_path
self.env = env
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 self.settings = settings
ssh-support: enabled full handling of all backends via SSH....
r2187 self.server_impl = None
def update_key_access_time(self, key_id):
ssh-wrapper: perf optimizations...
r4947 from rhodecode.model.meta import raw_query_executor, Base
table = Table('user_ssh_keys', Base.metadata, autoload=False)
apps: various fixes and improvements for python3
r5072 atime = datetime.datetime.utcnow()
ssh-wrapper: perf optimizations...
r4947 stmt = (
table.update()
.where(table.c.ssh_key_id == key_id)
apps: various fixes and improvements for python3
r5072 .values(accessed_on=atime)
# no MySQL Support for .returning :((
#.returning(table.c.accessed_on, table.c.ssh_key_fingerprint)
ssh-wrapper: perf optimizations...
r4947 )
apps: various fixes and improvements for python3
r5072 res_count = None
ssh-wrapper: perf optimizations...
r4947 with raw_query_executor() as session:
result = session.execute(stmt)
if result.rowcount:
apps: various fixes and improvements for python3
r5072 res_count = result.rowcount
ssh-wrapper: perf optimizations...
r4947
apps: various fixes and improvements for python3
r5072 if res_count:
log.debug('Update key id:`%s` access time', key_id)
ssh-wrapper: perf optimizations...
r4947
def get_user(self, user_id):
user = AttributeDict()
# lazy load db imports
from rhodecode.model.db import User
dbuser = User.get(user_id)
if not dbuser:
return None
user.user_id = dbuser.user_id
user.username = dbuser.username
user.auth_user = dbuser.AuthUser()
return user
ssh-support: enabled full handling of all backends via SSH....
r2187
def get_connection_info(self):
"""
connection_info
Identifies the client and server ends of the connection.
The variable contains four space-separated values: client IP address,
client port number, server IP address, and server port number.
"""
conn = dict(
client_ip=None,
client_port=None,
server_ip=None,
server_port=None,
)
info = self.connection_info.split(' ')
if len(info) == 4:
conn['client_ip'] = info[0]
conn['client_port'] = info[1]
conn['server_ip'] = info[2]
conn['server_port'] = info[3]
return conn
ssh: allow clone by ID via SSH operations.
r4644 def maybe_translate_repo_uid(self, repo_name):
ssh: handle subrepos better
r4703 _org_name = repo_name
if _org_name.startswith('_'):
# remove format of _ID/subrepo
_org_name = _org_name.split('/', 1)[0]
ssh: allow clone by ID via SSH operations.
r4644 if repo_name.startswith('_'):
from rhodecode.model.repo import RepoModel
ssh: handle subrepos better
r4703 org_repo_name = repo_name
log.debug('translating UID repo %s', org_repo_name)
ssh: allow clone by ID via SSH operations.
r4644 by_id_match = RepoModel().get_repo_by_id(repo_name)
if by_id_match:
repo_name = by_id_match.repo_name
ssh: handle subrepos better
r4703 log.debug('translation of UID repo %s got `%s`', org_repo_name, repo_name)
return repo_name, _org_name
ssh: allow clone by ID via SSH operations.
r4644
ssh-support: enabled full handling of all backends via SSH....
r2187 def get_repo_details(self, mode):
vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
repo_name = None
ssh: use pre-compiled backends for faster matching of vcs detection.
r4702 hg_match = self.hg_cmd_pat.match(self.command)
ssh-support: enabled full handling of all backends via SSH....
r2187 if hg_match is not None:
vcs_type = 'hg'
ssh: handle subrepos better
r4703 repo_id = hg_match.group(1).strip('/')
repo_name, org_name = self.maybe_translate_repo_uid(repo_id)
ssh-support: enabled full handling of all backends via SSH....
r2187 return vcs_type, repo_name, mode
ssh: use pre-compiled backends for faster matching of vcs detection.
r4702 git_match = self.git_cmd_pat.match(self.command)
ssh-support: enabled full handling of all backends via SSH....
r2187 if git_match is not None:
ssh: handle subrepos better
r4703 mode = git_match.group(1)
ssh-support: enabled full handling of all backends via SSH....
r2187 vcs_type = 'git'
ssh: handle subrepos better
r4703 repo_id = git_match.group(2).strip('/')
repo_name, org_name = self.maybe_translate_repo_uid(repo_id)
ssh-support: enabled full handling of all backends via SSH....
r2187 return vcs_type, repo_name, mode
ssh: use pre-compiled backends for faster matching of vcs detection.
r4702 svn_match = self.svn_cmd_pat.match(self.command)
ssh-support: enabled full handling of all backends via SSH....
r2187 if svn_match is not None:
vcs_type = 'svn'
dan
svn: fixed case of wrong extracted repository name for SSH backend. In cases...
r4281 # Repo name should be extracted from the input stream, we're unable to
# extract it at this point in execution
ssh-support: enabled full handling of all backends via SSH....
r2187 return vcs_type, repo_name, mode
return vcs_type, repo_name, mode
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 def serve(self, vcs, repo, mode, user, permissions, branch_permissions):
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 # TODO: remove this once we have .ini defined access path...
from rhodecode.model.scm import ScmModel
ssh-support: enabled full handling of all backends via SSH....
r2187 store = ScmModel().repos_path
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 check_branch_perms = False
detect_force_push = False
if branch_permissions:
check_branch_perms = True
detect_force_push = True
ssh-support: enabled full handling of all backends via SSH....
r2187 log.debug(
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 'VCS detected:`%s` mode: `%s` repo_name: %s, branch_permission_checks:%s',
vcs, mode, repo, check_branch_perms)
# detect if we have to check branch permissions
extras = {
'detect_force_push': detect_force_push,
'check_branch_perms': check_branch_perms,
feat(celery-hooks): added all needed changes to support new celery backend, removed DummyHooksCallbackDaemon, updated tests. Fixes: RCCE-55
r5298 'config': self.ini_path
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 }
ssh-support: enabled full handling of all backends via SSH....
r2187
if vcs == 'hg':
server = MercurialServer(
store=store, ini_path=self.ini_path,
repo_name=repo, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
ssh-support: enabled full handling of all backends via SSH....
r2187 self.server_impl = server
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 return server.run(tunnel_extras=extras)
ssh-support: enabled full handling of all backends via SSH....
r2187
elif vcs == 'git':
server = GitServer(
store=store, ini_path=self.ini_path,
repo_name=repo, repo_mode=mode, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
ssh-support: enabled full handling of all backends via SSH....
r2187 self.server_impl = server
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 return server.run(tunnel_extras=extras)
ssh-support: enabled full handling of all backends via SSH....
r2187
elif vcs == 'svn':
server = SubversionServer(
store=store, ini_path=self.ini_path,
repo_name=None, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
ssh-support: enabled full handling of all backends via SSH....
r2187 self.server_impl = server
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 return server.run(tunnel_extras=extras)
ssh-support: enabled full handling of all backends via SSH....
r2187
else:
modernize: updates for python3
r5095 raise Exception(f'Unrecognised VCS: {vcs}')
ssh-support: enabled full handling of all backends via SSH....
r2187
def wrap(self):
mode = self.mode
ssh-wrapper: perf optimizations...
r4947 username = self.username
ssh-support: enabled full handling of all backends via SSH....
r2187 user_id = self.user_id
key_id = self.key_id
shell = self.shell
scm_detected, scm_repo, scm_mode = self.get_repo_details(mode)
log.debug(
ssh-wrapper: perf optimizations...
r4947 'Mode: `%s` User: `name:%s : id:%s` Shell: `%s` SSH Command: `\"%s\"` '
ssh-support: enabled full handling of all backends via SSH....
r2187 'SCM_DETECTED: `%s` SCM Mode: `%s` SCM Repo: `%s`',
ssh-wrapper: perf optimizations...
r4947 mode, username, user_id, shell, self.command,
ssh-support: enabled full handling of all backends via SSH....
r2187 scm_detected, scm_mode, scm_repo)
ssh-wrapper: perf optimizations...
r4947 log.debug('SSH Connection info %s', self.get_connection_info())
ssh-support: enabled full handling of all backends via SSH....
r2187 # update last access time for this key
ssh-wrapper: perf optimizations...
r4947 if key_id:
self.update_key_access_time(key_id)
ssh-support: enabled full handling of all backends via SSH....
r2187
if shell and self.command is None:
dan
svn: fixed case of wrong extracted repository name for SSH backend. In cases...
r4281 log.info('Dropping to shell, no command given and shell is allowed')
ssh-support: enabled full handling of all backends via SSH....
r2187 os.execl('/bin/bash', '-l')
exit_code = 1
elif scm_detected:
ssh-wrapper: perf optimizations...
r4947 user = self.get_user(user_id)
ssh: prevent exceptions when user associated to stored old key is not found.
r2206 if not user:
log.warning('User with id %s not found', user_id)
exit_code = -1
return exit_code
ssh-wrapper: perf optimizations...
r4947 auth_user = user.auth_user
ssh-support: enabled full handling of all backends via SSH....
r2187 permissions = auth_user.permissions['repositories']
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 repo_branch_permissions = auth_user.get_branch_permissions(scm_repo)
ssh-support: enabled full handling of all backends via SSH....
r2187 try:
exit_code, is_updated = self.serve(
branch-permissions: enabled branch permissions checks for SSH backend.
r2982 scm_detected, scm_repo, scm_mode, user, permissions,
repo_branch_permissions)
ssh-support: enabled full handling of all backends via SSH....
r2187 except Exception:
log.exception('Error occurred during execution of SshWrapper')
exit_code = -1
elif self.command is None and shell is False:
log.error('No Command given.')
exit_code = -1
else:
dan
svn: fixed case of wrong extracted repository name for SSH backend. In cases...
r4281 log.error('Unhandled Command: "%s" Aborting.', self.command)
ssh-support: enabled full handling of all backends via SSH....
r2187 exit_code = -1
return exit_code
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314
class SshWrapperStandalone(SshWrapper):
"""
New version of SshWrapper designed to be depended only on service API
"""
repos_path = None
@staticmethod
def parse_user_related_data(user_data):
user = AttributeDict()
user.user_id = user_data['user_id']
user.username = user_data['username']
user.repo_permissions = user_data['repo_permissions']
user.branch_permissions = user_data['branch_permissions']
return user
def wrap(self):
mode = self.mode
username = self.username
user_id = self.user_id
shell = self.shell
scm_detected, scm_repo, scm_mode = self.get_repo_details(mode)
log.debug(
'Mode: `%s` User: `name:%s : id:%s` Shell: `%s` SSH Command: `\"%s\"` '
'SCM_DETECTED: `%s` SCM Mode: `%s` SCM Repo: `%s`',
mode, username, user_id, shell, self.command,
scm_detected, scm_mode, scm_repo)
log.debug('SSH Connection info %s', self.get_connection_info())
if shell and self.command is None:
log.info('Dropping to shell, no command given and shell is allowed')
os.execl('/bin/bash', '-l')
exit_code = 1
elif scm_detected:
refactor(ssh-wrapper): changed SSHVcsServer to SshVcsServer, updated call_service_api method.
r5326 data = call_service_api(self.settings, {
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314 "method": "service_get_data_for_ssh_wrapper",
"args": {"user_id": user_id, "repo_name": scm_repo, "key_id": self.key_id}
})
user = self.parse_user_related_data(data)
if not user:
log.warning('User with id %s not found', user_id)
exit_code = -1
return exit_code
self.repos_path = data['repos_path']
permissions = user.repo_permissions
repo_branch_permissions = user.branch_permissions
try:
exit_code, is_updated = self.serve(
scm_detected, scm_repo, scm_mode, user, permissions,
repo_branch_permissions)
except Exception:
log.exception('Error occurred during execution of SshWrapper')
exit_code = -1
elif self.command is None and shell is False:
log.error('No Command given.')
exit_code = -1
else:
log.error('Unhandled Command: "%s" Aborting.', self.command)
exit_code = -1
return exit_code
def maybe_translate_repo_uid(self, repo_name):
_org_name = repo_name
if _org_name.startswith('_'):
_org_name = _org_name.split('/', 1)[0]
if repo_name.startswith('_'):
org_repo_name = repo_name
log.debug('translating UID repo %s', org_repo_name)
refactor(ssh-wrapper): changed SSHVcsServer to SshVcsServer, updated call_service_api method.
r5326 by_id_match = call_service_api(self.settings, {
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314 'method': 'service_get_repo_name_by_id',
"args": {"repo_id": repo_name}
})
if by_id_match:
repo_name = by_id_match['repo_name']
log.debug('translation of UID repo %s got `%s`', org_repo_name, repo_name)
return repo_name, _org_name
def serve(self, vcs, repo, mode, user, permissions, branch_permissions):
store = self.repos_path
check_branch_perms = False
detect_force_push = False
if branch_permissions:
check_branch_perms = True
detect_force_push = True
log.debug(
'VCS detected:`%s` mode: `%s` repo_name: %s, branch_permission_checks:%s',
vcs, mode, repo, check_branch_perms)
# detect if we have to check branch permissions
extras = {
'detect_force_push': detect_force_push,
'check_branch_perms': check_branch_perms,
'config': self.ini_path
}
match vcs:
case 'hg':
server = MercurialServer(
store=store, ini_path=self.ini_path,
repo_name=repo, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314 case 'git':
server = GitServer(
store=store, ini_path=self.ini_path,
repo_name=repo, repo_mode=mode, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314 case 'svn':
server = SubversionServer(
store=store, ini_path=self.ini_path,
repo_name=None, user=user,
feat(ssh-wrapper-speedup): major rewrite of code to address imports problem with ssh-wrapper-v2...
r5325 user_permissions=permissions, settings=self.settings, env=self.env)
fix(ssh): Added alternative SshWrapper and changes needed to support it + service api. Fixes: RCCE-6
r5314 case _:
raise Exception(f'Unrecognised VCS: {vcs}')
self.server_impl = server
return server.run(tunnel_extras=extras)