svn: fixed issue with custom svn client user agents

This commit is contained in:
RhodeCode Admin 2025-11-28 11:32:10 +01:00
parent e7e12dfaf2
commit f172aa4144
2 changed files with 63 additions and 9 deletions

View file

@ -110,15 +110,19 @@ class SubversionTunnelWrapper(object):
signal.alarm(0)
return self._parse_first_client_response(first_response) if first_response else None
def patch_first_client_response(self, response, **kwargs):
def _prepare_first_client_response(self, response: dict, **kwargs) -> bytes:
version = response["version"]
capabilities = response["capabilities"]
client = response["client"] or b""
url = self._svn_bytes(response["url"])
ra_client = self._svn_bytes(response["ra_client"])
client = b" " + self._svn_bytes(response["client"]) or b""
buffer_ = b"( %b ( %b ) %b%b( %b) ) " % (version, capabilities, url, ra_client, client)
buffer_ = b"( %b ( %b ) %b%b(%b) )" % (version, capabilities, url, ra_client, client)
return buffer_
def patch_first_client_response(self, response, **kwargs):
buffer_ = self._prepare_first_client_response(response, **kwargs)
self.process.stdin.write(buffer_)
self.process.stdin.flush()
@ -162,8 +166,7 @@ class SubversionTunnelWrapper(object):
According to the Subversion RA protocol, the first request
should look like:
( version:number ( cap:word ... ) url:string ? ra-client:string
( ? client:string ) )
( version:number ( cap:word ... ) url:string ? ra-client:string ( ? client:string ) )
Please check https://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol
"""
@ -173,15 +176,14 @@ class SubversionTunnelWrapper(object):
ra_client_re = rb"(\d+\:(?P<ra_client>[\W\w]+)\s)"
client_re = rb"(\d+\:(?P<client>[\W\w]+)\s)*"
regex = re.compile(
rb"^\(\s%b\s%b\s%b\s%b"
rb"\(\s%b\)\s\)\s*$" % (version_re, capabilities_re, url_re, ra_client_re, client_re)
rb"^\(\s%b\s%b\s%b\s%b\(\s%b\)\s\)\s*$" % (version_re, capabilities_re, url_re, ra_client_re, client_re)
)
matcher = regex.match(buffer_)
return matcher.groupdict() if matcher else None
def _match_repo_name(self, url):
"""
Given an server url, try to match it against ALL known repository names.
Given a server url, try to match it against ALL known repository names.
This handles a tricky SVN case for SSH and subdir commits.
E.g if our repo name is my-svn-repo, a svn commit on file in a subdir would
result in the url with this subdir added.

View file

@ -21,7 +21,7 @@ import os
import mock
import pytest
from rhodecode.apps.ssh_support.lib.backends.svn import SubversionServer
from rhodecode.apps.ssh_support.lib.backends.svn import SubversionServer, SubversionTunnelWrapper
from rhodecode.apps.ssh_support.tests.conftest import plain_dummy_env, plain_dummy_user
@ -224,3 +224,55 @@ class TestSubversionServer(object):
exit_code = server.run(tunnel_extras={"config": server.ini_path})
assert exit_code == (1, False)
@pytest.mark.parametrize(
"first_resp, expected_match",
[
(
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( 24:TortoiseSVN-1.14.9.29743 ) )",
{
"version": b"2",
"capabilities": b"edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops",
"url": b"svn+ssh://rc@code.example.com/TestRepo/trunk",
"ra_client": b"SVN/1.14.5 (x64-microsoft-windows)",
"client": b"TortoiseSVN-1.14.9.29743",
},
),
(
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( ) )",
{
"version": b"2",
"capabilities": b"edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops",
"url": b"svn+ssh://rc@code.example.com/TestRepo/trunk",
"ra_client": b"SVN/1.14.5 (x64-microsoft-windows)",
"client": None,
},
),
],
)
def test__parse_first_client_response(self, svn_server, first_resp, expected_match):
server = svn_server.create()
tunnel = SubversionTunnelWrapper(server)
parsed = tunnel._parse_first_client_response(first_resp)
assert parsed == expected_match
@pytest.mark.parametrize(
"first_resp, expected_match",
[
(
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( 24:TortoiseSVN-1.14.9.29743 ) )",
None,
),
(
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( ) )",
None,
),
],
)
def test__parse_and_prepare_first_client_response(self, svn_server, first_resp, expected_match):
server = svn_server.create()
tunnel = SubversionTunnelWrapper(server)
parsed = tunnel._parse_first_client_response(first_resp)
first_response_prepared = tunnel._prepare_first_client_response(parsed)
assert first_response_prepared == first_resp