##// END OF EJS Templates
svn: fixed case of wrong extracted repository name for SSH backend. In cases...
dan -
r4281:5da17e74 default
parent child Browse files
Show More
@@ -91,7 +91,6 b' class SshWrapper(object):'
91 91
92 92 def get_repo_details(self, mode):
93 93 vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
94 mode = mode
95 94 repo_name = None
96 95
97 96 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
@@ -101,8 +100,7 b' class SshWrapper(object):'
101 100 repo_name = hg_match.group(1).strip('/')
102 101 return vcs_type, repo_name, mode
103 102
104 git_pattern = (
105 r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
103 git_pattern = r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$'
106 104 git_match = re.match(git_pattern, self.command)
107 105 if git_match is not None:
108 106 vcs_type = 'git'
@@ -115,7 +113,8 b' class SshWrapper(object):'
115 113
116 114 if svn_match is not None:
117 115 vcs_type = 'svn'
118 # Repo name should be extracted from the input stream
116 # Repo name should be extracted from the input stream, we're unable to
117 # extract it at this point in execution
119 118 return vcs_type, repo_name, mode
120 119
121 120 return vcs_type, repo_name, mode
@@ -188,8 +187,7 b' class SshWrapper(object):'
188 187 log.debug('SSH Connection info %s', self.get_connection_info())
189 188
190 189 if shell and self.command is None:
191 log.info(
192 'Dropping to shell, no command given and shell is allowed')
190 log.info('Dropping to shell, no command given and shell is allowed')
193 191 os.execl('/bin/bash', '-l')
194 192 exit_code = 1
195 193
@@ -216,8 +214,7 b' class SshWrapper(object):'
216 214 exit_code = -1
217 215
218 216 else:
219 log.error(
220 'Unhandled Command: "%s" Aborting.', self.command)
217 log.error('Unhandled Command: "%s" Aborting.', self.command)
221 218 exit_code = -1
222 219
223 220 return exit_code
@@ -66,9 +66,8 b' class VcsServer(object):'
66 66
67 67 def _check_permissions(self, action):
68 68 permission = self.user_permissions.get(self.repo_name)
69 log.debug(
70 'permission for %s on %s are: %s',
71 self.user, self.repo_name, permission)
69 log.debug('permission for %s on %s are: %s',
70 self.user, self.repo_name, permission)
72 71
73 72 if not permission:
74 73 log.error('user `%s` permissions to repo:%s are empty. Forbidding access.',
@@ -68,8 +68,7 b' class GitServer(VcsServer):'
68 68 self.store = store
69 69 self.ini_path = ini_path
70 70 self.repo_name = repo_name
71 self._path = self.git_path = config.get(
72 'app:main', 'ssh.executable.git')
71 self._path = self.git_path = config.get('app:main', 'ssh.executable.git')
73 72
74 73 self.repo_mode = repo_mode
75 74 self.tunnel = GitTunnelWrapper(server=self)
@@ -95,9 +95,8 b' class SubversionTunnelWrapper(object):'
95 95 signal.alarm(self.timeout)
96 96 first_response = self._read_first_client_response()
97 97 signal.alarm(0)
98 return (
99 self._parse_first_client_response(first_response)
100 if first_response else None)
98 return (self._parse_first_client_response(first_response)
99 if first_response else None)
101 100
102 101 def patch_first_client_response(self, response, **kwargs):
103 102 self.create_hooks_env()
@@ -112,9 +111,8 b' class SubversionTunnelWrapper(object):'
112 111 self.process.stdin.write(buffer_)
113 112
114 113 def fail(self, message):
115 print(
116 "( failure ( ( 210005 {message} 0: 0 ) ) )".format(
117 message=self._svn_string(message)))
114 print("( failure ( ( 210005 {message} 0: 0 ) ) )".format(
115 message=self._svn_string(message)))
118 116 self.remove_configs()
119 117 self.process.kill()
120 118 return 1
@@ -139,6 +137,7 b' class SubversionTunnelWrapper(object):'
139 137 brackets_stack.pop()
140 138 elif next_byte == " " and not brackets_stack:
141 139 break
140
142 141 return buffer_
143 142
144 143 def _parse_first_client_response(self, buffer_):
@@ -149,8 +148,7 b' class SubversionTunnelWrapper(object):'
149 148 ( version:number ( cap:word ... ) url:string ? ra-client:string
150 149 ( ? client:string ) )
151 150
152 Please check https://svn.apache.org/repos/asf/subversion/trunk/
153 subversion/libsvn_ra_svn/protocol
151 Please check https://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol
154 152 """
155 153 version_re = r'(?P<version>\d+)'
156 154 capabilities_re = r'\(\s(?P<capabilities>[\w\d\-\ ]+)\s\)'
@@ -163,8 +161,35 b' class SubversionTunnelWrapper(object):'
163 161 version=version_re, capabilities=capabilities_re,
164 162 url=url_re, ra_client=ra_client_re, client=client_re))
165 163 matcher = regex.match(buffer_)
164
166 165 return matcher.groupdict() if matcher else None
167 166
167 def _match_repo_name(self, url):
168 """
169 Given an server url, try to match it against ALL known repository names.
170 This handles a tricky SVN case for SSH and subdir commits.
171 E.g if our repo name is my-svn-repo, a svn commit on file in a subdir would
172 result in the url with this subdir added.
173 """
174 # case 1 direct match, we don't do any "heavy" lookups
175 if url in self.server.user_permissions:
176 return url
177
178 log.debug('Extracting repository name from subdir path %s', url)
179 # case 2 we check all permissions, and match closes possible case...
180 # NOTE(dan): In this case we only know that url has a subdir parts, it's safe
181 # to assume that it will have the repo name as prefix, we ensure the prefix
182 # for similar repositories isn't matched by adding a /
183 # e.g subgroup/repo-name/ and subgroup/repo-name-1/ would work correct.
184 for repo_name in self.server.user_permissions:
185 repo_name_prefix = repo_name + '/'
186 if url.startswith(repo_name_prefix):
187 log.debug('Found prefix %s match, returning proper repository name',
188 repo_name_prefix)
189 return repo_name
190
191 return
192
168 193 def run(self, extras):
169 194 action = 'pull'
170 195 self.create_svn_config()
@@ -175,7 +200,8 b' class SubversionTunnelWrapper(object):'
175 200 return self.fail("Repository name cannot be extracted")
176 201
177 202 url_parts = urlparse.urlparse(first_response['url'])
178 self.server.repo_name = url_parts.path.strip('/')
203
204 self.server.repo_name = self._match_repo_name(url_parts.path.strip('/'))
179 205
180 206 exit_code = self.server._check_permissions(action)
181 207 if exit_code:
@@ -200,10 +226,10 b' class SubversionServer(VcsServer):'
200 226 .__init__(user, user_permissions, config, env)
201 227 self.store = store
202 228 self.ini_path = ini_path
203 # this is set in .run() from input stream
229 # NOTE(dan): repo_name at this point is empty,
230 # this is set later in .run() based from parsed input stream
204 231 self.repo_name = repo_name
205 self._path = self.svn_path = config.get(
206 'app:main', 'ssh.executable.svn')
232 self._path = self.svn_path = config.get('app:main', 'ssh.executable.svn')
207 233
208 234 self.tunnel = SubversionTunnelWrapper(server=self)
209 235
@@ -89,6 +89,86 b' class TestSubversionServer(object):'
89 89 result = server._check_permissions(action)
90 90 assert result is code
91 91
92 @pytest.mark.parametrize('permissions, access_paths, expected_match', [
93 # not matched repository name
94 ({
95 'test-svn': ''
96 }, ['test-svn-1', 'test-svn-1/subpath'],
97 None),
98
99 # exact match
100 ({
101 'test-svn': ''
102 },
103 ['test-svn'],
104 'test-svn'),
105
106 # subdir commits
107 ({
108 'test-svn': ''
109 },
110 ['test-svn/foo',
111 'test-svn/foo/test-svn',
112 'test-svn/trunk/development.txt',
113 ],
114 'test-svn'),
115
116 # subgroups + similar patterns
117 ({
118 'test-svn': '',
119 'test-svn-1': '',
120 'test-svn-subgroup/test-svn': '',
121
122 },
123 ['test-svn-1',
124 'test-svn-1/foo/test-svn',
125 'test-svn-1/test-svn',
126 ],
127 'test-svn-1'),
128
129 # subgroups + similar patterns
130 ({
131 'test-svn-1': '',
132 'test-svn-10': '',
133 'test-svn-100': '',
134 },
135 ['test-svn-10',
136 'test-svn-10/foo/test-svn',
137 'test-svn-10/test-svn',
138 ],
139 'test-svn-10'),
140
141 # subgroups + similar patterns
142 ({
143 'name': '',
144 'nameContains': '',
145 'nameContainsThis': '',
146 },
147 ['nameContains',
148 'nameContains/This',
149 'nameContains/This/test-svn',
150 ],
151 'nameContains'),
152
153 # subgroups + similar patterns
154 ({
155 'test-svn': '',
156 'test-svn-1': '',
157 'test-svn-subgroup/test-svn': '',
158
159 },
160 ['test-svn-subgroup/test-svn',
161 'test-svn-subgroup/test-svn/foo/test-svn',
162 'test-svn-subgroup/test-svn/trunk/example.txt',
163 ],
164 'test-svn-subgroup/test-svn'),
165 ])
166 def test_repo_extraction_on_subdir(self, svn_server, permissions, access_paths, expected_match):
167 server = svn_server.create(user_permissions=permissions)
168 for path in access_paths:
169 repo_name = server.tunnel._match_repo_name(path)
170 assert repo_name == expected_match
171
92 172 def test_run_returns_executes_command(self, svn_server):
93 173 server = svn_server.create()
94 174 from rhodecode.apps.ssh_support.lib.backends.svn import SubversionTunnelWrapper
General Comments 0
You need to be logged in to leave comments. Login now