Merge ~ilasc/launchpad:add-mp-url-to-git-push into launchpad:master
- Git
- lp:~ilasc/launchpad
- add-mp-url-to-git-push
- Merge into master
Status: | Merged |
---|---|
Approved by: | Ioana Lasc |
Approved revision: | 9d74d487fa58d8c3cea4d78768aad92bc2ffd2f6 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~ilasc/launchpad:add-mp-url-to-git-push |
Merge into: | launchpad:master |
Diff against target: |
386 lines (+308/-2) 3 files modified
lib/lp/code/interfaces/gitapi.py (+13/-0) lib/lp/code/xmlrpc/git.py (+46/-1) lib/lp/code/xmlrpc/tests/test_git.py (+249/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+377044@code.launchpad.net |
Commit message
Add MP URL to git push
Description of the change
Ioana Lasc (ilasc) wrote : | # |
Colin Watson (cjwatson) : | # |
- d9bdd8f... by Ioana Lasc
-
Addressed review comments and added unit tests.
Ioana Lasc (ilasc) wrote : | # |
Addressed code review comments and added a couple of Unit Tests.
Still working on moving the check for the default branch to the turnip side.
- f76a6d6... by Ioana Lasc
-
Move check for default branch from LP to turnip
Ioana Lasc (ilasc) wrote : | # |
Moved the check for default branch to Turnip per our conversation.
Goes hand in hand with this on the Turnip side: https:/
Both now ready for review.
Colin Watson (cjwatson) : | # |
- d36f50c... by Ioana Lasc
-
Add authentication for getMergeProposalURL
Split up the implementation into `getMergePropos
alURL` and
`_getMergeProposalURL` to add authetication for the
getMergeProposalURL endpoint on the Git API. As LP returns
data back to Turnip this needs to be an authenticated
operation. Added unit tests for several scenarios.
Needs more comments and more tests for other scenarios. - c0ec754... by Ioana Lasc
-
Add unit tests for getMergeProposa
lURL. After changing to an authenticated getMergeProposalURL
more unit tests were required to cover the authenticated
user and code imports scenarios.
Ioana Lasc (ilasc) wrote : | # |
Added more Unit Tests and addressed almost all comments apart from
from six.moves.
can't get the code to compile with it - which is a known PyCharm issue, tried running cli tests but code doesn't run in cli either and make lint fails.
I believe there is also an issue with Unauthorized scenario in getMergeProposa
Comment I'm referring to in git.py line 564:
# XXX cjwatson 2019-05-09: It would be simpler to just raise
# this directly, but turnip won't handle it very gracefully at
# the moment. It's possible to reach this by being very unlucky
# about the timing of a push.
Colin Watson (cjwatson) wrote : | # |
Not being able to use six.moves.
- 6f934f1... by Ioana Lasc
-
Refactor Unit Tests for GIT API getMergeProposalURL
Ioana Lasc (ilasc) wrote : | # |
Thank you Colin. MP is now ready for review in tandem with: https:/
Colin Watson (cjwatson) : | # |
- 678cfce... by Ioana Lasc
-
Address code review comments
Added minor changes to unit tests suggested in code review
for the add-mp-url-to- git-push branch.
Ioana Lasc (ilasc) wrote : | # |
Thanks Colin, last round of updates done and noted that we don't land 377045 until this makes it to Prod.
Colin Watson (cjwatson) : | # |
- 9d74d48... by Ioana Lasc
-
Remove commented out code
Removed commented code left in lp/code/
xmlrpc/ tests/test_ git.py.
Preview Diff
1 | diff --git a/lib/lp/code/interfaces/gitapi.py b/lib/lp/code/interfaces/gitapi.py | |||
2 | index c7db491..c289dee 100644 | |||
3 | --- a/lib/lp/code/interfaces/gitapi.py | |||
4 | +++ b/lib/lp/code/interfaces/gitapi.py | |||
5 | @@ -79,3 +79,16 @@ class IGitAPI(Interface): | |||
6 | 79 | 79 | ||
7 | 80 | :returns: A list of rules for the user in the specified repository | 80 | :returns: A list of rules for the user in the specified repository |
8 | 81 | """ | 81 | """ |
9 | 82 | |||
10 | 83 | def getMergeProposalURL(translated_path, branch, auth_params): | ||
11 | 84 | """Return the URL for a merge proposal. | ||
12 | 85 | |||
13 | 86 | When a `branch` that is not the default branch in a repository | ||
14 | 87 | is pushed, the URL where the merge proposal for that branch can | ||
15 | 88 | be opened will be generated and returned. | ||
16 | 89 | |||
17 | 90 | :returns: The URL to register a merge proposal for the branch in the | ||
18 | 91 | specified repository. A `GitRepositoryNotFound` fault is returned | ||
19 | 92 | if no repository can be found for 'translated_path', | ||
20 | 93 | or an `Unauthorized` fault for unauthorized push attempts. | ||
21 | 94 | """ | ||
22 | diff --git a/lib/lp/code/xmlrpc/git.py b/lib/lp/code/xmlrpc/git.py | |||
23 | index cbf9205..b34307a 100644 | |||
24 | --- a/lib/lp/code/xmlrpc/git.py | |||
25 | +++ b/lib/lp/code/xmlrpc/git.py | |||
26 | @@ -15,6 +15,7 @@ import uuid | |||
27 | 15 | from pymacaroons import Macaroon | 15 | from pymacaroons import Macaroon |
28 | 16 | import six | 16 | import six |
29 | 17 | from six.moves import xmlrpc_client | 17 | from six.moves import xmlrpc_client |
30 | 18 | from six.moves.urllib.parse import quote | ||
31 | 18 | import transaction | 19 | import transaction |
32 | 19 | from zope.component import ( | 20 | from zope.component import ( |
33 | 20 | ComponentLookupError, | 21 | ComponentLookupError, |
34 | @@ -72,7 +73,10 @@ from lp.services.macaroons.interfaces import ( | |||
35 | 72 | IMacaroonIssuer, | 73 | IMacaroonIssuer, |
36 | 73 | NO_USER, | 74 | NO_USER, |
37 | 74 | ) | 75 | ) |
39 | 75 | from lp.services.webapp import LaunchpadXMLRPCView | 76 | from lp.services.webapp import ( |
40 | 77 | canonical_url, | ||
41 | 78 | LaunchpadXMLRPCView, | ||
42 | 79 | ) | ||
43 | 76 | from lp.services.webapp.authorization import check_permission | 80 | from lp.services.webapp.authorization import check_permission |
44 | 77 | from lp.services.webapp.errorlog import ScriptRequest | 81 | from lp.services.webapp.errorlog import ScriptRequest |
45 | 78 | from lp.xmlrpc import faults | 82 | from lp.xmlrpc import faults |
46 | @@ -427,6 +431,47 @@ class GitAPI(LaunchpadXMLRPCView): | |||
47 | 427 | logger.info("notify succeeded") | 431 | logger.info("notify succeeded") |
48 | 428 | 432 | ||
49 | 429 | @return_fault | 433 | @return_fault |
50 | 434 | def _getMergeProposalURL(self, requester, translated_path, branch, | ||
51 | 435 | auth_params): | ||
52 | 436 | if requester == LAUNCHPAD_ANONYMOUS: | ||
53 | 437 | requester = None | ||
54 | 438 | repository = removeSecurityProxy( | ||
55 | 439 | getUtility(IGitLookup).getByHostingPath(translated_path)) | ||
56 | 440 | if repository is None: | ||
57 | 441 | raise faults.GitRepositoryNotFound(translated_path) | ||
58 | 442 | |||
59 | 443 | verified = self._verifyAuthParams(requester, repository, auth_params) | ||
60 | 444 | if verified is not None and verified.user is NO_USER: | ||
61 | 445 | # Showing a merge proposal URL may be useful to ordinary users, | ||
62 | 446 | # but it doesn't make sense in the context of an internal service. | ||
63 | 447 | return None | ||
64 | 448 | |||
65 | 449 | # We assemble the URL this way here because the ref may not exist yet. | ||
66 | 450 | base_url = canonical_url(repository, rootsite='code') | ||
67 | 451 | mp_url = "%s/+ref/%s/+register-merge" % ( | ||
68 | 452 | base_url, quote(branch)) | ||
69 | 453 | return mp_url | ||
70 | 454 | |||
71 | 455 | def getMergeProposalURL(self, translated_path, branch, auth_params): | ||
72 | 456 | """See `IGitAPI`.""" | ||
73 | 457 | logger = self._getLogger(auth_params.get("request-id")) | ||
74 | 458 | requester_id = _get_requester_id(auth_params) | ||
75 | 459 | logger.info( | ||
76 | 460 | "Request received: getMergeProposalURL('%s, %s') for %s", | ||
77 | 461 | translated_path, branch, requester_id) | ||
78 | 462 | result = run_with_login( | ||
79 | 463 | requester_id, self._getMergeProposalURL, | ||
80 | 464 | translated_path, branch, auth_params) | ||
81 | 465 | if isinstance(result, xmlrpc_client.Fault): | ||
82 | 466 | logger.error("getMergeProposalURL failed: %r", result) | ||
83 | 467 | else: | ||
84 | 468 | # The result of getMergeProposalURL is not sensitive for logging | ||
85 | 469 | # purposes (it may refer to private artifacts, but contains no | ||
86 | 470 | # credentials, only the merge proposal URL). | ||
87 | 471 | logger.info("getMergeProposalURL succeeded: %s" % result) | ||
88 | 472 | return result | ||
89 | 473 | |||
90 | 474 | @return_fault | ||
91 | 430 | def _authenticateWithPassword(self, username, password): | 475 | def _authenticateWithPassword(self, username, password): |
92 | 431 | """Authenticate a user by username and password. | 476 | """Authenticate a user by username and password. |
93 | 432 | 477 | ||
94 | diff --git a/lib/lp/code/xmlrpc/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py | |||
95 | index df65f0e..6f12f1f 100644 | |||
96 | --- a/lib/lp/code/xmlrpc/tests/test_git.py | |||
97 | +++ b/lib/lp/code/xmlrpc/tests/test_git.py | |||
98 | @@ -1,4 +1,4 @@ | |||
100 | 1 | # Copyright 2015-2019 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2020 Canonical Ltd. This software is licensed under the |
101 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
102 | 3 | 3 | ||
103 | 4 | """Tests for the internal Git API.""" | 4 | """Tests for the internal Git API.""" |
104 | @@ -11,6 +11,7 @@ from fixtures import FakeLogger | |||
105 | 11 | from pymacaroons import Macaroon | 11 | from pymacaroons import Macaroon |
106 | 12 | import six | 12 | import six |
107 | 13 | from six.moves import xmlrpc_client | 13 | from six.moves import xmlrpc_client |
108 | 14 | from six.moves.urllib.parse import quote | ||
109 | 14 | from testtools.matchers import ( | 15 | from testtools.matchers import ( |
110 | 15 | Equals, | 16 | Equals, |
111 | 16 | IsInstance, | 17 | IsInstance, |
112 | @@ -51,6 +52,7 @@ from lp.services.macaroons.interfaces import ( | |||
113 | 51 | NO_USER, | 52 | NO_USER, |
114 | 52 | ) | 53 | ) |
115 | 53 | from lp.services.macaroons.model import MacaroonIssuerBase | 54 | from lp.services.macaroons.model import MacaroonIssuerBase |
116 | 55 | from lp.services.webapp import canonical_url | ||
117 | 54 | from lp.services.webapp.escaping import html_escape | 56 | from lp.services.webapp.escaping import html_escape |
118 | 55 | from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS | 57 | from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS |
119 | 56 | from lp.testing import ( | 58 | from lp.testing import ( |
120 | @@ -326,6 +328,15 @@ class TestGitAPIMixin: | |||
121 | 326 | ]) | 328 | ]) |
122 | 327 | for ref_path, ref_permissions in permissions.items()))) | 329 | for ref_path, ref_permissions in permissions.items()))) |
123 | 328 | 330 | ||
124 | 331 | def assertHasMergeProposalURL(self, repository, | ||
125 | 332 | pushed_branch, auth_params): | ||
126 | 333 | base_url = canonical_url(repository, rootsite='code') | ||
127 | 334 | expected_mp_url = "%s/+ref/%s/+register-merge" % ( | ||
128 | 335 | base_url, quote(pushed_branch)) | ||
129 | 336 | result = self.git_api.getMergeProposalURL( | ||
130 | 337 | repository.getInternalPath(), pushed_branch, auth_params) | ||
131 | 338 | self.assertEqual(expected_mp_url, result) | ||
132 | 339 | |||
133 | 329 | def test_translatePath_private_repository(self): | 340 | def test_translatePath_private_repository(self): |
134 | 330 | requester = self.factory.makePerson() | 341 | requester = self.factory.makePerson() |
135 | 331 | repository = removeSecurityProxy( | 342 | repository = removeSecurityProxy( |
136 | @@ -636,6 +647,13 @@ class TestGitAPIMixin: | |||
137 | 636 | faults.GitRepositoryNotFound("nonexistent"), None, | 647 | faults.GitRepositoryNotFound("nonexistent"), None, |
138 | 637 | "checkRefPermissions", "nonexistent", [], {"uid": requester.id}) | 648 | "checkRefPermissions", "nonexistent", [], {"uid": requester.id}) |
139 | 638 | 649 | ||
140 | 650 | def test_getMergeProposalURL__nonexistent_repository(self): | ||
141 | 651 | requester = self.factory.makePerson() | ||
142 | 652 | self.assertFault( | ||
143 | 653 | faults.GitRepositoryNotFound("nonexistent"), None, | ||
144 | 654 | "getMergeProposalURL", "nonexistent", 'branch1', | ||
145 | 655 | {"uid": requester.id}) | ||
146 | 656 | |||
147 | 639 | 657 | ||
148 | 640 | class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): | 658 | class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
149 | 641 | """Tests for the implementation of `IGitAPI`.""" | 659 | """Tests for the implementation of `IGitAPI`.""" |
150 | @@ -1265,6 +1283,236 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): | |||
151 | 1265 | requester, path, permission="read", | 1283 | requester, path, permission="read", |
152 | 1266 | macaroon_raw=macaroon.serialize()) | 1284 | macaroon_raw=macaroon.serialize()) |
153 | 1267 | 1285 | ||
154 | 1286 | def test_getMergeProposalURL_user(self): | ||
155 | 1287 | # A merge proposal URL is returned by LP for a non-default branch | ||
156 | 1288 | # pushed by a user that has their ordinary privileges on the | ||
157 | 1289 | # corresponding repository. | ||
158 | 1290 | requester_owner = self.factory.makePerson() | ||
159 | 1291 | repository = self.factory.makeGitRepository(owner=requester_owner) | ||
160 | 1292 | self.factory.makeGitRefs( | ||
161 | 1293 | repository=repository, paths=[u'refs/heads/master']) | ||
162 | 1294 | removeSecurityProxy(repository).default_branch = u'refs/heads/master' | ||
163 | 1295 | pushed_branch = 'branch1' | ||
164 | 1296 | self.assertHasMergeProposalURL(repository, pushed_branch, | ||
165 | 1297 | {"uid": requester_owner.id}) | ||
166 | 1298 | |||
167 | 1299 | # Turnip verifies if the pushed branch is the default branch | ||
168 | 1300 | # in a repository and calls LP only for non default branches. | ||
169 | 1301 | # Consequently LP will process any incoming branch from Turnip | ||
170 | 1302 | # as being non default and produce a merge proposal URL for it. | ||
171 | 1303 | self.factory.makeGitRefs( | ||
172 | 1304 | repository=repository, paths=[u'refs/heads/%s' % pushed_branch]) | ||
173 | 1305 | removeSecurityProxy(repository).default_branch = ( | ||
174 | 1306 | u'refs/heads/%s' % pushed_branch) | ||
175 | 1307 | self.assertHasMergeProposalURL(repository, pushed_branch, | ||
176 | 1308 | {"uid": requester_owner.id}) | ||
177 | 1309 | |||
178 | 1310 | requester_non_owner = self.factory.makePerson() | ||
179 | 1311 | self.assertHasMergeProposalURL(repository, pushed_branch, | ||
180 | 1312 | {"uid": requester_non_owner.id}) | ||
181 | 1313 | |||
182 | 1314 | def test_getMergeProposalURL_user_macaroon(self): | ||
183 | 1315 | # The merge proposal URL is returned by LP for a non-default branch | ||
184 | 1316 | # pushed by a user with a suitable macaroon that | ||
185 | 1317 | # has their ordinary privileges on the corresponding repository. | ||
186 | 1318 | |||
187 | 1319 | self.pushConfig("codehosting", git_macaroon_secret_key="some-secret") | ||
188 | 1320 | requester = self.factory.makePerson() | ||
189 | 1321 | repository = self.factory.makeGitRepository(owner=requester) | ||
190 | 1322 | issuer = getUtility(IMacaroonIssuer, "git-repository") | ||
191 | 1323 | self.factory.makeGitRefs( | ||
192 | 1324 | repository=repository, paths=[u'refs/heads/master']) | ||
193 | 1325 | removeSecurityProxy(repository).default_branch = u'refs/heads/master' | ||
194 | 1326 | |||
195 | 1327 | pushed_branch = 'branch1' | ||
196 | 1328 | with person_logged_in(requester): | ||
197 | 1329 | macaroon = removeSecurityProxy(issuer).issueMacaroon( | ||
198 | 1330 | repository, user=requester) | ||
199 | 1331 | auth_params = _make_auth_params(requester, | ||
200 | 1332 | macaroon_raw=macaroon.serialize()) | ||
201 | 1333 | self.assertHasMergeProposalURL(repository, pushed_branch, auth_params) | ||
202 | 1334 | |||
203 | 1335 | def test_getMergeProposalURL_user_mismatch(self): | ||
204 | 1336 | # getMergeProposalURL refuses macaroons in the case where the | ||
205 | 1337 | # user doesn't match what the issuer claims was verified. (We use a | ||
206 | 1338 | # dummy issuer for this, since this is a stopgap check to defend | ||
207 | 1339 | # against issuer bugs) | ||
208 | 1340 | |||
209 | 1341 | issuer = DummyMacaroonIssuer() | ||
210 | 1342 | # Claim to be the code-import-job issuer. This is a bit weird, but | ||
211 | 1343 | # it gets us past the difficulty that only certain named issuers are | ||
212 | 1344 | # allowed to confer write permissions. | ||
213 | 1345 | issuer.identifier = "code-import-job" | ||
214 | 1346 | self.useFixture(ZopeUtilityFixture( | ||
215 | 1347 | issuer, IMacaroonIssuer, name="code-import-job")) | ||
216 | 1348 | requesters = [self.factory.makePerson() for _ in range(2)] | ||
217 | 1349 | owner = self.factory.makeTeam(members=requesters) | ||
218 | 1350 | repository = self.factory.makeGitRepository(owner=owner) | ||
219 | 1351 | self.factory.makeGitRefs( | ||
220 | 1352 | repository=repository, paths=[u'refs/heads/master']) | ||
221 | 1353 | removeSecurityProxy(repository).default_branch = u'refs/heads/master' | ||
222 | 1354 | pushed_branch = 'branch1' | ||
223 | 1355 | macaroon = issuer.issueMacaroon(repository) | ||
224 | 1356 | |||
225 | 1357 | for verified_user, authorized, unauthorized in ( | ||
226 | 1358 | (requesters[0], [requesters[0]], | ||
227 | 1359 | [LAUNCHPAD_SERVICES, requesters[1], None]), | ||
228 | 1360 | ([None, NO_USER], [], | ||
229 | 1361 | [LAUNCHPAD_SERVICES] + requesters + [None]), | ||
230 | 1362 | ): | ||
231 | 1363 | issuer._verified_user = verified_user | ||
232 | 1364 | for requester in authorized: | ||
233 | 1365 | login(ANONYMOUS) | ||
234 | 1366 | auth_params = _make_auth_params( | ||
235 | 1367 | requester, macaroon_raw=macaroon.serialize()) | ||
236 | 1368 | self.assertHasMergeProposalURL(repository, | ||
237 | 1369 | pushed_branch, auth_params) | ||
238 | 1370 | for requester in unauthorized: | ||
239 | 1371 | login(ANONYMOUS) | ||
240 | 1372 | auth_params = _make_auth_params( | ||
241 | 1373 | requester, macaroon_raw=macaroon.serialize()) | ||
242 | 1374 | |||
243 | 1375 | self.assertFault( | ||
244 | 1376 | faults.Unauthorized, None, | ||
245 | 1377 | "getMergeProposalURL", repository.getInternalPath(), | ||
246 | 1378 | pushed_branch, auth_params) | ||
247 | 1379 | |||
248 | 1380 | def test_getMergeProposalURL_code_import(self): | ||
249 | 1381 | # A merge proposal URL from LP to Turnip is not returned for | ||
250 | 1382 | # code import job as there is no User at the other end. | ||
251 | 1383 | |||
252 | 1384 | issuer = DummyMacaroonIssuer() | ||
253 | 1385 | # Claim to be the code-import-job issuer. This is a bit weird, but | ||
254 | 1386 | # it gets us past the difficulty that only certain named issuers are | ||
255 | 1387 | # allowed to confer write permissions. | ||
256 | 1388 | self.pushConfig( | ||
257 | 1389 | "launchpad", internal_macaroon_secret_key="some-secret") | ||
258 | 1390 | machine = self.factory.makeCodeImportMachine(set_online=True) | ||
259 | 1391 | code_imports = [ | ||
260 | 1392 | self.factory.makeCodeImport( | ||
261 | 1393 | target_rcs_type=TargetRevisionControlSystems.GIT) | ||
262 | 1394 | for _ in range(2)] | ||
263 | 1395 | with celebrity_logged_in("vcs_imports"): | ||
264 | 1396 | jobs = [ | ||
265 | 1397 | self.factory.makeCodeImportJob(code_import=code_import) | ||
266 | 1398 | for code_import in code_imports] | ||
267 | 1399 | issuer = getUtility(IMacaroonIssuer, "code-import-job") | ||
268 | 1400 | macaroons = [ | ||
269 | 1401 | removeSecurityProxy(issuer).issueMacaroon(job) for job in jobs] | ||
270 | 1402 | repository = code_imports[0].git_repository | ||
271 | 1403 | auth_params = _make_auth_params( | ||
272 | 1404 | LAUNCHPAD_SERVICES, macaroons[0].serialize()) | ||
273 | 1405 | pushed_branch = 'branch1' | ||
274 | 1406 | self.assertFault( | ||
275 | 1407 | faults.Unauthorized, None, | ||
276 | 1408 | "getMergeProposalURL", repository.getInternalPath(), | ||
277 | 1409 | pushed_branch, auth_params) | ||
278 | 1410 | |||
279 | 1411 | with celebrity_logged_in("vcs_imports"): | ||
280 | 1412 | getUtility(ICodeImportJobWorkflow).startJob(jobs[0], machine) | ||
281 | 1413 | auth_params = _make_auth_params( | ||
282 | 1414 | LAUNCHPAD_SERVICES, macaroons[0].serialize()) | ||
283 | 1415 | |||
284 | 1416 | self.assertFault( | ||
285 | 1417 | faults.Unauthorized, None, | ||
286 | 1418 | "getMergeProposalURL", | ||
287 | 1419 | removeSecurityProxy(repository).getInternalPath(), | ||
288 | 1420 | pushed_branch, auth_params) | ||
289 | 1421 | |||
290 | 1422 | auth_params = _make_auth_params( | ||
291 | 1423 | LAUNCHPAD_SERVICES, macaroons[1].serialize()) | ||
292 | 1424 | |||
293 | 1425 | self.assertFault( | ||
294 | 1426 | faults.Unauthorized, None, | ||
295 | 1427 | "getMergeProposalURL", | ||
296 | 1428 | removeSecurityProxy(repository).getInternalPath(), | ||
297 | 1429 | pushed_branch, auth_params) | ||
298 | 1430 | |||
299 | 1431 | auth_params = _make_auth_params( | ||
300 | 1432 | LAUNCHPAD_SERVICES, | ||
301 | 1433 | macaroon_raw=Macaroon( | ||
302 | 1434 | location=config.vhost.mainsite.hostname, identifier="another", | ||
303 | 1435 | key="another-secret").serialize()) | ||
304 | 1436 | self.assertFault( | ||
305 | 1437 | faults.Unauthorized, None, | ||
306 | 1438 | "getMergeProposalURL", | ||
307 | 1439 | removeSecurityProxy(repository).getInternalPath(), | ||
308 | 1440 | pushed_branch, auth_params) | ||
309 | 1441 | |||
310 | 1442 | auth_params = _make_auth_params( | ||
311 | 1443 | LAUNCHPAD_SERVICES, macaroon_raw="nonsense") | ||
312 | 1444 | |||
313 | 1445 | self.assertFault( | ||
314 | 1446 | faults.Unauthorized, None, | ||
315 | 1447 | "getMergeProposalURL", | ||
316 | 1448 | removeSecurityProxy(repository).getInternalPath(), | ||
317 | 1449 | pushed_branch, auth_params) | ||
318 | 1450 | |||
319 | 1451 | def test_getMergeProposalURL_private_code_import(self): | ||
320 | 1452 | # We do not send the Merge Proposal URL back | ||
321 | 1453 | # to a Code Import Job. | ||
322 | 1454 | |||
323 | 1455 | self.pushConfig( | ||
324 | 1456 | "launchpad", internal_macaroon_secret_key="some-secret") | ||
325 | 1457 | machine = self.factory.makeCodeImportMachine(set_online=True) | ||
326 | 1458 | code_imports = [ | ||
327 | 1459 | self.factory.makeCodeImport( | ||
328 | 1460 | target_rcs_type=TargetRevisionControlSystems.GIT) | ||
329 | 1461 | for _ in range(2)] | ||
330 | 1462 | private_repository = code_imports[0].git_repository | ||
331 | 1463 | removeSecurityProxy(private_repository).transitionToInformationType( | ||
332 | 1464 | InformationType.PRIVATESECURITY, private_repository.owner) | ||
333 | 1465 | with celebrity_logged_in("vcs_imports"): | ||
334 | 1466 | jobs = [ | ||
335 | 1467 | self.factory.makeCodeImportJob(code_import=code_import) | ||
336 | 1468 | for code_import in code_imports] | ||
337 | 1469 | issuer = getUtility(IMacaroonIssuer, "code-import-job") | ||
338 | 1470 | macaroons = [ | ||
339 | 1471 | removeSecurityProxy(issuer).issueMacaroon(job) for job in jobs] | ||
340 | 1472 | repository = code_imports[0].git_repository | ||
341 | 1473 | auth_params = _make_auth_params( | ||
342 | 1474 | LAUNCHPAD_SERVICES, macaroons[0].serialize()) | ||
343 | 1475 | pushed_branch = 'branch1' | ||
344 | 1476 | |||
345 | 1477 | self.assertFault( | ||
346 | 1478 | faults.Unauthorized, None, | ||
347 | 1479 | "getMergeProposalURL", | ||
348 | 1480 | removeSecurityProxy(repository).getInternalPath(), | ||
349 | 1481 | pushed_branch, auth_params) | ||
350 | 1482 | |||
351 | 1483 | with celebrity_logged_in("vcs_imports"): | ||
352 | 1484 | getUtility(ICodeImportJobWorkflow).startJob(jobs[0], machine) | ||
353 | 1485 | |||
354 | 1486 | auth_params = _make_auth_params( | ||
355 | 1487 | LAUNCHPAD_SERVICES, macaroons[1].serialize()) | ||
356 | 1488 | |||
357 | 1489 | self.assertFault( | ||
358 | 1490 | faults.Unauthorized, None, | ||
359 | 1491 | "getMergeProposalURL", | ||
360 | 1492 | removeSecurityProxy(repository).getInternalPath(), | ||
361 | 1493 | pushed_branch, auth_params) | ||
362 | 1494 | |||
363 | 1495 | auth_params = _make_auth_params( | ||
364 | 1496 | LAUNCHPAD_SERVICES, macaroon_raw=Macaroon( | ||
365 | 1497 | location=config.vhost.mainsite.hostname, | ||
366 | 1498 | identifier="another", | ||
367 | 1499 | key="another-secret").serialize()) | ||
368 | 1500 | |||
369 | 1501 | self.assertFault( | ||
370 | 1502 | faults.Unauthorized, None, | ||
371 | 1503 | "getMergeProposalURL", | ||
372 | 1504 | removeSecurityProxy(repository).getInternalPath(), | ||
373 | 1505 | pushed_branch, auth_params) | ||
374 | 1506 | |||
375 | 1507 | auth_params = _make_auth_params( | ||
376 | 1508 | LAUNCHPAD_SERVICES, macaroon_raw="nonsense") | ||
377 | 1509 | |||
378 | 1510 | self.assertFault( | ||
379 | 1511 | faults.Unauthorized, None, | ||
380 | 1512 | "getMergeProposalURL", | ||
381 | 1513 | removeSecurityProxy(repository).getInternalPath(), | ||
382 | 1514 | pushed_branch, auth_params) | ||
383 | 1515 | |||
384 | 1268 | def test_notify(self): | 1516 | def test_notify(self): |
385 | 1269 | # The notify call creates a GitRefScanJob. | 1517 | # The notify call creates a GitRefScanJob. |
386 | 1270 | repository = self.factory.makeGitRepository() | 1518 | repository = self.factory.makeGitRepository() |
This needs Unit Tests and imports re-arranging - the rest is definitely in need a of a critical eye.