Merge lp:~cjwatson/launchpad/git-xmlrpc-auth-params into lp:launchpad

Proposed by Colin Watson on 2016-10-05
Status: Merged
Merged at revision: 18225
Proposed branch: lp:~cjwatson/launchpad/git-xmlrpc-auth-params
Merge into: lp:launchpad
Diff against target: 274 lines (+76/-47)
2 files modified
lib/lp/code/xmlrpc/git.py (+11/-3)
lib/lp/code/xmlrpc/tests/test_git.py (+65/-44)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-xmlrpc-auth-params
Reviewer Review Type Date Requested Status
William Grant code 2016-10-05 Approve on 2016-10-06
Review via email: mp+307694@code.launchpad.net

Commit Message

Support passing GitAPI.translatePath authentication parameters as a dict.

Description of the Change

Support passing GitAPI.translatePath authentication parameters as a dict. Once this is deployed, turnip will be able to pass additional data from the authentication stage through to translatePath; this will be needed for code imports, where we want to be able to say that a client has successfully authenticated but is only authorised to push to a certain repository.

To post a comment you must log in.
William Grant (wgrant) wrote :

I don't think we use "uid" anywhere else, but it seems reasonably sensible.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/xmlrpc/git.py'
2--- lib/lp/code/xmlrpc/git.py 2016-10-05 09:15:05 +0000
3+++ lib/lp/code/xmlrpc/git.py 2016-10-06 11:17:54 +0000
4@@ -229,7 +229,7 @@
5 raise
6
7 @return_fault
8- def _translatePath(self, requester, path, permission, can_authenticate):
9+ def _translatePath(self, requester, path, permission, auth_params):
10 if requester == LAUNCHPAD_ANONYMOUS:
11 requester = None
12 try:
13@@ -247,20 +247,28 @@
14 # Turn lookup errors for anonymous HTTP requests into
15 # "authorisation required", so that the user-agent has a
16 # chance to try HTTP basic auth.
17+ can_authenticate = auth_params.get("can-authenticate", False)
18 if can_authenticate and requester is None:
19 raise faults.Unauthorized()
20 else:
21 raise
22
23- def translatePath(self, path, permission, requester_id, can_authenticate):
24+ def translatePath(self, path, permission, *auth_args):
25 """See `IGitAPI`."""
26+ if len(auth_args) == 1:
27+ auth_params = auth_args[0]
28+ requester_id = auth_params.get("uid")
29+ else:
30+ requester_id, can_authenticate = auth_args
31+ auth_params = {
32+ "uid": requester_id, "can-authenticate": can_authenticate}
33 if requester_id is None:
34 requester_id = LAUNCHPAD_ANONYMOUS
35 if isinstance(path, str):
36 path = path.decode('utf-8')
37 return run_with_login(
38 requester_id, self._translatePath,
39- path.strip("/"), permission, can_authenticate)
40+ path.strip("/"), permission, auth_params)
41
42 def notify(self, translated_path):
43 """See `IGitAPI`."""
44
45=== modified file 'lib/lp/code/xmlrpc/tests/test_git.py'
46--- lib/lp/code/xmlrpc/tests/test_git.py 2016-10-05 09:15:05 +0000
47+++ lib/lp/code/xmlrpc/tests/test_git.py 2016-10-06 11:17:54 +0000
48@@ -5,16 +5,16 @@
49
50 __metaclass__ = type
51
52+from testscenarios import (
53+ load_tests_apply_scenarios,
54+ WithScenarios,
55+ )
56 from zope.component import getUtility
57 from zope.security.proxy import removeSecurityProxy
58
59 from lp.app.enums import InformationType
60 from lp.code.enums import GitRepositoryType
61 from lp.code.errors import GitRepositoryCreationFault
62-from lp.code.interfaces.codehosting import (
63- LAUNCHPAD_ANONYMOUS,
64- LAUNCHPAD_SERVICES,
65- )
66 from lp.code.interfaces.gitcollection import IAllGitRepositories
67 from lp.code.interfaces.gitjob import IGitRefScanJobSource
68 from lp.code.interfaces.gitrepository import (
69@@ -39,22 +39,36 @@
70 from lp.xmlrpc import faults
71
72
73-class TestGitAPIMixin:
74+class TestGitAPIMixin(WithScenarios):
75 """Helper methods for `IGitAPI` tests, and security-relevant tests."""
76
77+ scenarios = [
78+ ("auth_params_flat", {"auth_params_dict": False}),
79+ ("auth_params_dict", {"auth_params_dict": True}),
80+ ]
81+
82 def setUp(self):
83 super(TestGitAPIMixin, self).setUp()
84 self.git_api = GitAPI(None, None)
85 self.hosting_fixture = self.useFixture(GitHostingFixture())
86 self.repository_set = getUtility(IGitRepositorySet)
87
88+ def _translatePath(self, path, permission, auth_params):
89+ if self.auth_params_dict:
90+ return self.git_api.translatePath(path, permission, auth_params)
91+ else:
92+ return self.git_api.translatePath(
93+ path, permission, auth_params.get("uid"),
94+ auth_params.get("can-authenticate", False))
95+
96 def assertGitRepositoryNotFound(self, requester, path, permission="read",
97 can_authenticate=False):
98 """Assert that the given path cannot be translated."""
99- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
100+ if requester is not None:
101 requester = requester.id
102- fault = self.git_api.translatePath(
103- path, permission, requester, can_authenticate)
104+ fault = self._translatePath(
105+ path, permission,
106+ {"uid": requester, "can-authenticate": can_authenticate})
107 self.assertEqual(
108 faults.GitRepositoryNotFound(path.strip("/")), fault)
109
110@@ -62,29 +76,32 @@
111 message="Permission denied.",
112 permission="read", can_authenticate=False):
113 """Assert that looking at the given path returns PermissionDenied."""
114- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
115+ if requester is not None:
116 requester = requester.id
117- fault = self.git_api.translatePath(
118- path, permission, requester, can_authenticate)
119+ fault = self._translatePath(
120+ path, permission,
121+ {"uid": requester, "can-authenticate": can_authenticate})
122 self.assertEqual(faults.PermissionDenied(message), fault)
123
124 def assertUnauthorized(self, requester, path,
125 message="Authorisation required.",
126 permission="read", can_authenticate=False):
127 """Assert that looking at the given path returns Unauthorized."""
128- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
129+ if requester is not None:
130 requester = requester.id
131- fault = self.git_api.translatePath(
132- path, permission, requester, can_authenticate)
133+ fault = self._translatePath(
134+ path, permission,
135+ {"uid": requester, "can-authenticate": can_authenticate})
136 self.assertEqual(faults.Unauthorized(message), fault)
137
138 def assertNotFound(self, requester, path, message, permission="read",
139 can_authenticate=False):
140 """Assert that looking at the given path returns NotFound."""
141- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
142+ if requester is not None:
143 requester = requester.id
144- fault = self.git_api.translatePath(
145- path, permission, requester, can_authenticate)
146+ fault = self._translatePath(
147+ path, permission,
148+ {"uid": requester, "can-authenticate": can_authenticate})
149 self.assertEqual(faults.NotFound(message), fault)
150
151 def assertInvalidSourcePackageName(self, requester, path, name,
152@@ -92,28 +109,31 @@
153 can_authenticate=False):
154 """Assert that looking at the given path returns
155 InvalidSourcePackageName."""
156- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
157+ if requester is not None:
158 requester = requester.id
159- fault = self.git_api.translatePath(
160- path, permission, requester, can_authenticate)
161+ fault = self._translatePath(
162+ path, permission,
163+ {"uid": requester, "can-authenticate": can_authenticate})
164 self.assertEqual(faults.InvalidSourcePackageName(name), fault)
165
166 def assertInvalidBranchName(self, requester, path, message,
167 permission="read", can_authenticate=False):
168 """Assert that looking at the given path returns InvalidBranchName."""
169- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
170+ if requester is not None:
171 requester = requester.id
172- fault = self.git_api.translatePath(
173- path, permission, requester, can_authenticate)
174+ fault = self._translatePath(
175+ path, permission,
176+ {"uid": requester, "can-authenticate": can_authenticate})
177 self.assertEqual(faults.InvalidBranchName(Exception(message)), fault)
178
179 def assertOopsOccurred(self, requester, path,
180 permission="read", can_authenticate=False):
181 """Assert that looking at the given path OOPSes."""
182- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
183+ if requester is not None:
184 requester = requester.id
185- fault = self.git_api.translatePath(
186- path, permission, requester, can_authenticate)
187+ fault = self._translatePath(
188+ path, permission,
189+ {"uid": requester, "can-authenticate": can_authenticate})
190 self.assertIsInstance(fault, faults.OopsOccurred)
191 prefix = (
192 "An unexpected error has occurred while creating a Git "
193@@ -124,10 +144,11 @@
194 def assertTranslates(self, requester, path, repository, writable,
195 permission="read", can_authenticate=False,
196 trailing="", private=False):
197- if requester not in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
198+ if requester is not None:
199 requester = requester.id
200- translation = self.git_api.translatePath(
201- path, permission, requester, can_authenticate)
202+ translation = self._translatePath(
203+ path, permission,
204+ {"uid": requester, "can-authenticate": can_authenticate})
205 login(ANONYMOUS)
206 self.assertEqual(
207 {"path": repository.getInternalPath(), "writable": writable,
208@@ -136,12 +157,13 @@
209
210 def assertCreates(self, requester, path, can_authenticate=False,
211 private=False):
212- if requester in (LAUNCHPAD_ANONYMOUS, LAUNCHPAD_SERVICES):
213+ if requester is None:
214 requester_id = requester
215 else:
216 requester_id = requester.id
217- translation = self.git_api.translatePath(
218- path, "write", requester_id, can_authenticate)
219+ translation = self._translatePath(
220+ path, "write",
221+ {"uid": requester_id, "can-authenticate": can_authenticate})
222 login(ANONYMOUS)
223 repository = getUtility(IGitRepositorySet).getByPath(
224 requester, path.lstrip("/"))
225@@ -185,10 +207,8 @@
226 self.factory.makeGitRepository(
227 information_type=InformationType.USERDATA))
228 path = u"/%s" % repository.unique_name
229- self.assertGitRepositoryNotFound(
230- LAUNCHPAD_ANONYMOUS, path, can_authenticate=False)
231- self.assertUnauthorized(
232- LAUNCHPAD_ANONYMOUS, path, can_authenticate=True)
233+ self.assertGitRepositoryNotFound(None, path, can_authenticate=False)
234+ self.assertUnauthorized(None, path, can_authenticate=True)
235
236 def test_translatePath_team_unowned(self):
237 requester = self.factory.makePerson()
238@@ -298,11 +318,9 @@
239 repository = self.factory.makeGitRepository()
240 path = u"/%s" % repository.unique_name
241 self.assertTranslates(
242- LAUNCHPAD_ANONYMOUS, path, repository, False,
243- can_authenticate=False)
244+ None, path, repository, False, can_authenticate=False)
245 self.assertTranslates(
246- LAUNCHPAD_ANONYMOUS, path, repository, False,
247- can_authenticate=True)
248+ None, path, repository, False, can_authenticate=True)
249
250 def test_translatePath_owned(self):
251 requester = self.factory.makePerson()
252@@ -400,11 +418,11 @@
253 # Anonymous users cannot create repositories.
254 project = self.factory.makeProject()
255 self.assertGitRepositoryNotFound(
256- LAUNCHPAD_ANONYMOUS, u"/%s" % project.name,
257- permission="write", can_authenticate=False)
258+ None, u"/%s" % project.name, permission="write",
259+ can_authenticate=False)
260 self.assertUnauthorized(
261- LAUNCHPAD_ANONYMOUS, u"/%s" % project.name,
262- permission="write", can_authenticate=True)
263+ None, u"/%s" % project.name, permission="write",
264+ can_authenticate=True)
265
266 def test_translatePath_create_invalid_namespace(self):
267 # Trying to create a repository at a path that isn't valid for Git
268@@ -657,3 +675,6 @@
269 """
270
271 layer = AppServerLayer
272+
273+
274+load_tests = load_tests_apply_scenarios