Files
@ 719ed95c437e
Branch filter:
Location: kallithea/kallithea/lib/vcs/backends/ssh.py
719ed95c437e
4.4 KiB
text/x-python
ssh: keep track of latest use of SSH keys
Based on work by Ilya Beda <ir4y.ix@gmail.com> on
https://bitbucket.org/ir4y/rhodecode/commits/branch/ssh_server_support , also
heavily modified by Mads Kiilerich.
Based on work by Ilya Beda <ir4y.ix@gmail.com> on
https://bitbucket.org/ir4y/rhodecode/commits/branch/ssh_server_support , also
heavily modified by Mads Kiilerich.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | # -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
vcs.backends.ssh
~~~~~~~~~~~~~~~~~
SSH backend for all available SCMs
"""
import sys
import datetime
import logging
from kallithea.model.db import Repository, User, UserSshKeys
from kallithea.model.meta import Session
from kallithea.lib.auth import HasPermissionAnyMiddleware, AuthUser
from kallithea.lib.utils2 import safe_str, set_hook_environment
log = logging.getLogger(__name__)
class BaseSshHandler(object):
# Protocol for setting properties:
# Set by sub class:
# vcs_type: 'hg' or 'git'
# Set by make() / __init__():
# repo_name: requested repo name - only validated by serve()
# Set by serve() - must not be accessed before:
# db_repo: repository db object
# authuser: user that has been authenticated - like request.authuser ... which isn't used here
# allow_push: false for read-only access to the repo
# Set defaults, in case .exit should be called early
vcs_type = None
repo_name = None
@staticmethod
def make(ssh_command):
"""Factory function. Given a command as invoked over SSH (and preserved
in SSH_ORIGINAL_COMMAND when run as authorized_keys command), return a
handler if the command looks ok, else return None.
"""
raise NotImplementedError
def serve(self, user_id, key_id, client_ip):
"""Verify basic sanity of the repository, and that the user is
valid and has access - then serve the native VCS protocol for
repository access."""
dbuser = User.get(user_id)
if dbuser is None:
self.exit('User %r not found' % user_id)
self.authuser = AuthUser.make(dbuser=dbuser, ip_addr=client_ip)
log.info('Authorized user %s from SSH %s trusting user id %s and key id %s for %r', dbuser, client_ip, user_id, key_id, self.repo_name)
if self.authuser is None: # not ok ... but already kind of authenticated by SSH ... but not really not authorized ...
self.exit('User %s from %s cannot be authorized' % (dbuser.username, client_ip))
ssh_key = UserSshKeys.get(key_id)
if ssh_key is None:
self.exit('SSH key %r not found' % key_id)
ssh_key.last_seen = datetime.datetime.now()
Session().commit()
if HasPermissionAnyMiddleware('repository.write',
'repository.admin')(self.authuser, self.repo_name):
self.allow_push = True
elif HasPermissionAnyMiddleware('repository.read')(self.authuser, self.repo_name):
self.allow_push = False
else:
self.exit('Access to %r denied' % safe_str(self.repo_name))
self.db_repo = Repository.get_by_repo_name(self.repo_name)
if self.db_repo is None:
self.exit("Repository '%s' not found" % self.repo_name)
assert self.db_repo.repo_name == self.repo_name
# Set global hook environment up for 'push' actions.
# If pull actions should be served, the actual hook invocation will be
# hardcoded to 'pull' when log_pull_action is invoked (directly on Git,
# or through the Mercurial 'outgoing' hook).
# For push actions, the action in global hook environment is used (in
# handle_git_post_receive when it is called as Git post-receive hook,
# or in log_push_action through the Mercurial 'changegroup' hook).
set_hook_environment(self.authuser.username, client_ip, self.repo_name, self.vcs_type, 'push')
return self._serve()
def _serve(self):
"""Serve the native protocol for repository access."""
raise NotImplementedError
def exit(self, error):
log.info('abort serving %s %s: %s', self.vcs_type, self.repo_name, error)
sys.stderr.write('abort: %s\n' % error)
sys.exit(1)
|