changeset 8739:f3e91cd075eb

vcs: move ssh handlers out of "backends" The ssh handlers are much more high-level than the low-level backends ... and not used as a part of the "backends" concept at all.
author Mads Kiilerich <mads@kiilerich.com>
date Tue, 20 Oct 2020 00:54:59 +0200
parents 7b809e4a1ea5
children 28b845dca1fd
files kallithea/bin/kallithea_cli_ssh.py kallithea/lib/vcs/backends/git/ssh.py kallithea/lib/vcs/backends/hg/ssh.py kallithea/lib/vcs/backends/ssh.py kallithea/lib/vcs/ssh/__init__.py kallithea/lib/vcs/ssh/base.py kallithea/lib/vcs/ssh/git.py kallithea/lib/vcs/ssh/hg.py
diffstat 7 files changed, 255 insertions(+), 255 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/bin/kallithea_cli_ssh.py	Wed Nov 04 14:30:48 2020 +0100
+++ b/kallithea/bin/kallithea_cli_ssh.py	Tue Oct 20 00:54:59 2020 +0200
@@ -22,8 +22,8 @@
 import kallithea
 import kallithea.bin.kallithea_cli_base as cli_base
 from kallithea.lib.utils2 import asbool
-from kallithea.lib.vcs.backends.git.ssh import GitSshHandler
-from kallithea.lib.vcs.backends.hg.ssh import MercurialSshHandler
+from kallithea.lib.vcs.ssh.git import GitSshHandler
+from kallithea.lib.vcs.ssh.hg import MercurialSshHandler
 from kallithea.model.ssh_key import SshKeyModel, SshKeyModelException
 
 
--- a/kallithea/lib/vcs/backends/git/ssh.py	Wed Nov 04 14:30:48 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-# -*- 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/>.
-
-import logging
-import os
-
-from kallithea.lib.hooks import log_pull_action
-from kallithea.lib.utils import make_ui
-from kallithea.lib.vcs.backends.ssh import BaseSshHandler
-
-
-log = logging.getLogger(__name__)
-
-
-class GitSshHandler(BaseSshHandler):
-    vcs_type = 'git'
-
-    @classmethod
-    def make(cls, ssh_command_parts):
-        r"""
-        >>> import shlex
-
-        >>> GitSshHandler.make(shlex.split("git-upload-pack '/foo bar'")).repo_name
-        'foo bar'
-        >>> GitSshHandler.make(shlex.split("git-upload-pack '/foo bar'")).verb
-        'git-upload-pack'
-        >>> GitSshHandler.make(shlex.split(" git-upload-pack /blåbærgrød ")).repo_name # might not be necessary to support no quoting ... but we can
-        'bl\xe5b\xe6rgr\xf8d'
-        >>> GitSshHandler.make(shlex.split('''git-upload-pack "/foo'bar"''')).repo_name
-        "foo'bar"
-        >>> GitSshHandler.make(shlex.split("git-receive-pack '/foo'")).repo_name
-        'foo'
-        >>> GitSshHandler.make(shlex.split("git-receive-pack '/foo'")).verb
-        'git-receive-pack'
-
-        >>> GitSshHandler.make(shlex.split("/bin/git-upload-pack '/foo'")) # ssh-serve will report 'SSH command %r is not supported'
-        >>> GitSshHandler.make(shlex.split('''git-upload-pack /foo bar''')) # ssh-serve will report 'SSH command %r is not supported'
-        >>> shlex.split("git-upload-pack '/foo'bar' x") # ssh-serve will report: Error parsing SSH command "...": No closing quotation
-        Traceback (most recent call last):
-        ValueError: No closing quotation
-        >>> GitSshHandler.make(shlex.split('hg -R foo serve --stdio')) # not handled here
-        """
-        if (len(ssh_command_parts) == 2 and
-            ssh_command_parts[0] in ['git-upload-pack', 'git-receive-pack'] and
-            ssh_command_parts[1].startswith('/')
-        ):
-            return cls(ssh_command_parts[1][1:], ssh_command_parts[0])
-
-        return None
-
-    def __init__(self, repo_name, verb):
-        BaseSshHandler.__init__(self, repo_name)
-        self.verb = verb
-
-    def _serve(self):
-        if self.verb == 'git-upload-pack': # action 'pull'
-            # base class called set_hook_environment - action is hardcoded to 'pull'
-            log_pull_action(ui=make_ui(), repo=self.db_repo.scm_instance._repo)
-        else: # probably verb 'git-receive-pack', action 'push'
-            if not self.allow_push:
-                self.exit('Push access to %r denied' % self.repo_name)
-            # Note: push logging is handled by Git post-receive hook
-
-        # git shell is not a real shell but use shell inspired quoting *inside* the argument.
-        # Per https://github.com/git/git/blob/v2.22.0/quote.c#L12 :
-        # The path must be "'" quoted, but "'" and "!" must exit the quoting and be "\" escaped
-        quoted_abspath = "'%s'" % self.db_repo.repo_full_path.replace("'", r"'\''").replace("!", r"'\!'")
-        newcmd = ['git', 'shell', '-c', "%s %s" % (self.verb, quoted_abspath)]
-        log.debug('Serving: %s', newcmd)
-        os.execvp(newcmd[0], newcmd)
-        self.exit("Failed to exec 'git' as %s" % newcmd)
--- a/kallithea/lib/vcs/backends/hg/ssh.py	Wed Nov 04 14:30:48 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-# -*- 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/>.
-
-import logging
-
-import mercurial.hg
-import mercurial.wireprotoserver
-
-from kallithea.lib.utils import make_ui
-from kallithea.lib.vcs.backends.ssh import BaseSshHandler
-from kallithea.lib.vcs.utils import safe_bytes
-
-
-log = logging.getLogger(__name__)
-
-
-class MercurialSshHandler(BaseSshHandler):
-    vcs_type = 'hg'
-
-    @classmethod
-    def make(cls, ssh_command_parts):
-        r"""
-        >>> import shlex
-
-        >>> MercurialSshHandler.make(shlex.split('hg -R "foo bar" serve --stdio')).repo_name
-        'foo bar'
-        >>> MercurialSshHandler.make(shlex.split(' hg -R blåbærgrød serve --stdio ')).repo_name
-        'bl\xe5b\xe6rgr\xf8d'
-        >>> MercurialSshHandler.make(shlex.split('''hg -R 'foo"bar' serve --stdio''')).repo_name
-        'foo"bar'
-
-        >>> MercurialSshHandler.make(shlex.split('/bin/hg -R "foo" serve --stdio'))
-        >>> MercurialSshHandler.make(shlex.split('''hg -R "foo"bar" serve --stdio''')) # ssh-serve will report: Error parsing SSH command "...": invalid syntax
-        Traceback (most recent call last):
-        ValueError: No closing quotation
-        >>> MercurialSshHandler.make(shlex.split('git-upload-pack "/foo"')) # not handled here
-        """
-        if ssh_command_parts[:2] == ['hg', '-R'] and ssh_command_parts[3:] == ['serve', '--stdio']:
-            return cls(ssh_command_parts[2])
-
-        return None
-
-    def _serve(self):
-        # Note: we want a repo with config based on .hg/hgrc and can thus not use self.db_repo.scm_instance._repo.ui
-        baseui = make_ui(repo_path=self.db_repo.repo_full_path)
-        if not self.allow_push:
-            baseui.setconfig(b'hooks', b'pretxnopen._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
-            baseui.setconfig(b'hooks', b'prepushkey._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
-
-        repo = mercurial.hg.repository(baseui, safe_bytes(self.db_repo.repo_full_path))
-        log.debug("Starting Mercurial sshserver for %s", self.db_repo.repo_full_path)
-        mercurial.wireprotoserver.sshserver(baseui, repo).serve_forever()
--- a/kallithea/lib/vcs/backends/ssh.py	Wed Nov 04 14:30:48 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-# -*- 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 datetime
-import logging
-import sys
-
-from kallithea.lib.auth import AuthUser, HasPermissionAnyMiddleware
-from kallithea.lib.utils2 import set_hook_environment
-from kallithea.model import db, meta
-
-
-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 __init__(self, repo_name):
-        self.repo_name = repo_name.rstrip('/')
-
-    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 = db.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 = db.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()
-        meta.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' % self.repo_name)
-
-        self.db_repo = db.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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/lib/vcs/ssh/base.py	Tue Oct 20 00:54:59 2020 +0200
@@ -0,0 +1,108 @@
+# -*- 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 datetime
+import logging
+import sys
+
+from kallithea.lib.auth import AuthUser, HasPermissionAnyMiddleware
+from kallithea.lib.utils2 import set_hook_environment
+from kallithea.model import db, meta
+
+
+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 __init__(self, repo_name):
+        self.repo_name = repo_name.rstrip('/')
+
+    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 = db.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 = db.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()
+        meta.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' % self.repo_name)
+
+        self.db_repo = db.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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/lib/vcs/ssh/git.py	Tue Oct 20 00:54:59 2020 +0200
@@ -0,0 +1,82 @@
+# -*- 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/>.
+
+import logging
+import os
+
+from kallithea.lib.hooks import log_pull_action
+from kallithea.lib.utils import make_ui
+from kallithea.lib.vcs.ssh import base
+
+
+log = logging.getLogger(__name__)
+
+
+class GitSshHandler(base.BaseSshHandler):
+    vcs_type = 'git'
+
+    @classmethod
+    def make(cls, ssh_command_parts):
+        r"""
+        >>> import shlex
+
+        >>> GitSshHandler.make(shlex.split("git-upload-pack '/foo bar'")).repo_name
+        'foo bar'
+        >>> GitSshHandler.make(shlex.split("git-upload-pack '/foo bar'")).verb
+        'git-upload-pack'
+        >>> GitSshHandler.make(shlex.split(" git-upload-pack /blåbærgrød ")).repo_name # might not be necessary to support no quoting ... but we can
+        'bl\xe5b\xe6rgr\xf8d'
+        >>> GitSshHandler.make(shlex.split('''git-upload-pack "/foo'bar"''')).repo_name
+        "foo'bar"
+        >>> GitSshHandler.make(shlex.split("git-receive-pack '/foo'")).repo_name
+        'foo'
+        >>> GitSshHandler.make(shlex.split("git-receive-pack '/foo'")).verb
+        'git-receive-pack'
+
+        >>> GitSshHandler.make(shlex.split("/bin/git-upload-pack '/foo'")) # ssh-serve will report 'SSH command %r is not supported'
+        >>> GitSshHandler.make(shlex.split('''git-upload-pack /foo bar''')) # ssh-serve will report 'SSH command %r is not supported'
+        >>> shlex.split("git-upload-pack '/foo'bar' x") # ssh-serve will report: Error parsing SSH command "...": No closing quotation
+        Traceback (most recent call last):
+        ValueError: No closing quotation
+        >>> GitSshHandler.make(shlex.split('hg -R foo serve --stdio')) # not handled here
+        """
+        if (len(ssh_command_parts) == 2 and
+            ssh_command_parts[0] in ['git-upload-pack', 'git-receive-pack'] and
+            ssh_command_parts[1].startswith('/')
+        ):
+            return cls(ssh_command_parts[1][1:], ssh_command_parts[0])
+
+        return None
+
+    def __init__(self, repo_name, verb):
+        base.BaseSshHandler.__init__(self, repo_name)
+        self.verb = verb
+
+    def _serve(self):
+        if self.verb == 'git-upload-pack': # action 'pull'
+            # base class called set_hook_environment - action is hardcoded to 'pull'
+            log_pull_action(ui=make_ui(), repo=self.db_repo.scm_instance._repo)
+        else: # probably verb 'git-receive-pack', action 'push'
+            if not self.allow_push:
+                self.exit('Push access to %r denied' % self.repo_name)
+            # Note: push logging is handled by Git post-receive hook
+
+        # git shell is not a real shell but use shell inspired quoting *inside* the argument.
+        # Per https://github.com/git/git/blob/v2.22.0/quote.c#L12 :
+        # The path must be "'" quoted, but "'" and "!" must exit the quoting and be "\" escaped
+        quoted_abspath = "'%s'" % self.db_repo.repo_full_path.replace("'", r"'\''").replace("!", r"'\!'")
+        newcmd = ['git', 'shell', '-c', "%s %s" % (self.verb, quoted_abspath)]
+        log.debug('Serving: %s', newcmd)
+        os.execvp(newcmd[0], newcmd)
+        self.exit("Failed to exec 'git' as %s" % newcmd)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/lib/vcs/ssh/hg.py	Tue Oct 20 00:54:59 2020 +0200
@@ -0,0 +1,63 @@
+# -*- 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/>.
+
+import logging
+
+import mercurial.hg
+import mercurial.wireprotoserver
+
+from kallithea.lib.utils import make_ui
+from kallithea.lib.vcs.ssh import base
+from kallithea.lib.vcs.utils import safe_bytes
+
+
+log = logging.getLogger(__name__)
+
+
+class MercurialSshHandler(base.BaseSshHandler):
+    vcs_type = 'hg'
+
+    @classmethod
+    def make(cls, ssh_command_parts):
+        r"""
+        >>> import shlex
+
+        >>> MercurialSshHandler.make(shlex.split('hg -R "foo bar" serve --stdio')).repo_name
+        'foo bar'
+        >>> MercurialSshHandler.make(shlex.split(' hg -R blåbærgrød serve --stdio ')).repo_name
+        'bl\xe5b\xe6rgr\xf8d'
+        >>> MercurialSshHandler.make(shlex.split('''hg -R 'foo"bar' serve --stdio''')).repo_name
+        'foo"bar'
+
+        >>> MercurialSshHandler.make(shlex.split('/bin/hg -R "foo" serve --stdio'))
+        >>> MercurialSshHandler.make(shlex.split('''hg -R "foo"bar" serve --stdio''')) # ssh-serve will report: Error parsing SSH command "...": invalid syntax
+        Traceback (most recent call last):
+        ValueError: No closing quotation
+        >>> MercurialSshHandler.make(shlex.split('git-upload-pack "/foo"')) # not handled here
+        """
+        if ssh_command_parts[:2] == ['hg', '-R'] and ssh_command_parts[3:] == ['serve', '--stdio']:
+            return cls(ssh_command_parts[2])
+
+        return None
+
+    def _serve(self):
+        # Note: we want a repo with config based on .hg/hgrc and can thus not use self.db_repo.scm_instance._repo.ui
+        baseui = make_ui(repo_path=self.db_repo.repo_full_path)
+        if not self.allow_push:
+            baseui.setconfig(b'hooks', b'pretxnopen._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
+            baseui.setconfig(b'hooks', b'prepushkey._ssh_reject', b'python:kallithea.lib.hooks.rejectpush')
+
+        repo = mercurial.hg.repository(baseui, safe_bytes(self.db_repo.repo_full_path))
+        log.debug("Starting Mercurial sshserver for %s", self.db_repo.repo_full_path)
+        mercurial.wireprotoserver.sshserver(baseui, repo).serve_forever()