Merge lp:~facundo/canonical-identity-provider/multiple-discharges into lp:canonical-identity-provider/release

Proposed by Facundo Batista
Status: Merged
Approved by: Facundo Batista
Approved revision: no longer in the source branch.
Merged at revision: 1428
Proposed branch: lp:~facundo/canonical-identity-provider/multiple-discharges
Merge into: lp:canonical-identity-provider/release
Diff against target: 107 lines (+63/-10)
2 files modified
src/api/v20/handlers.py (+25/-10)
src/api/v20/tests/test_handlers.py (+38/-0)
To merge this branch: bzr merge lp:~facundo/canonical-identity-provider/multiple-discharges
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+292173@code.launchpad.net

Commit message

Support multiple macaroons in the discharge endpoint.

Description of the change

Support multiple macaroons in the discharge endpoint.

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) :
Revision history for this message
Facundo Batista (facundo) wrote :

Answered..

Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/api/v20/handlers.py'
--- src/api/v20/handlers.py 2016-03-22 21:06:07 +0000
+++ src/api/v20/handlers.py 2016-04-18 16:05:29 +0000
@@ -438,12 +438,21 @@
438 try:438 try:
439 email = data['email']439 email = data['email']
440 password = data['password']440 password = data['password']
441 root_macaroon_raw = data['macaroon']
442 except KeyError:441 except KeyError:
443 expected = set(('email', 'password', 'macaroon'))442 expected = {'email', 'password'}
444 missing = dict((k, [FIELD_REQUIRED]) for k in expected - set(data))443 missing = dict((k, [FIELD_REQUIRED]) for k in expected - set(data))
445 return errors.INVALID_DATA(**missing)444 return errors.INVALID_DATA(**missing)
446445
446 if 'macaroon' in data:
447 simple_macaroon = True
448 root_macaroons_info = [(None, data['macaroon'])]
449 elif 'macaroons' in data:
450 simple_macaroon = False
451 root_macaroons_info = data['macaroons']
452 else:
453 missing = {'macaroon or macaroons': [FIELD_REQUIRED]}
454 return errors.INVALID_DATA(**missing)
455
447 account = None456 account = None
448 response = None457 response = None
449 try:458 try:
@@ -481,16 +490,22 @@
481 if response is not None:490 if response is not None:
482 return response491 return response
483492
484 try:493 all_discharges = []
485 discharge = auth.build_discharge_macaroon(494 for mac_id, root_macaroon_raw in root_macaroons_info:
486 account, root_macaroon_raw)495 try:
487 except ValidationError:496 discharge = auth.build_discharge_macaroon(
488 return errors.INVALID_DATA()497 account, root_macaroon_raw)
489 except AuthenticationError:498 except ValidationError:
490 return errors.INVALID_CREDENTIALS()499 return errors.INVALID_DATA()
500 except AuthenticationError:
501 return errors.INVALID_CREDENTIALS()
502 all_discharges.append((mac_id, discharge.serialize()))
491503
492 response = rc.ALL_OK504 response = rc.ALL_OK
493 response.content = dict(discharge_macaroon=discharge.serialize())505 if simple_macaroon:
506 response.content = dict(discharge_macaroon=all_discharges[0][1])
507 else:
508 response.content = dict(discharge_macaroons=all_discharges)
494509
495 log_type = AuthLogType.API_MACAROON_DISCHARGE_NEW510 log_type = AuthLogType.API_MACAROON_DISCHARGE_NEW
496 login_succeeded.send(sender=self, user=account, request=request,511 login_succeeded.send(sender=self, user=account, request=request,
497512
=== modified file 'src/api/v20/tests/test_handlers.py'
--- src/api/v20/tests/test_handlers.py 2016-04-01 19:35:14 +0000
+++ src/api/v20/tests/test_handlers.py 2016-04-18 16:05:29 +0000
@@ -2344,6 +2344,44 @@
2344 expected_status_code=401,2344 expected_status_code=401,
2345 check_login_failed=False)2345 check_login_failed=False)
23462346
2347 def test_missing_macaroons(self):
2348 data = dict(email='foo@bar.com', password='foobar123')
2349 extra = {'macaroon or macaroons': [handlers.FIELD_REQUIRED]}
2350 self.assert_failed_login('INVALID_DATA', data, extra=extra,
2351 expected_status_code=400,
2352 check_login_failed=False)
2353
2354 def test_multiple_macaroons(self):
2355 # build *several* macaroons and send them all at once
2356 roots, rkeys = zip(*[self.build_macaroon() for _ in range(3)])
2357 ids = range(3)
2358 roots = dict(zip(ids, roots))
2359 rkeys = dict(zip(ids, rkeys))
2360 macaroons = [(mac_id, m.serialize()) for mac_id, m in roots.items()]
2361 data = dict(email='foo@bar.com', password='foobar123',
2362 macaroons=macaroons)
2363 json_body = self.do_post(data=data)
2364
2365 # get all the discharges and verify their integrity
2366 verifying_function = lambda c: True
2367 for mac_id, discharge_macaroon_raw in json_body['discharge_macaroons']:
2368 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
2369 root_macaroon = roots[mac_id]
2370 key = rkeys[mac_id]
2371 v = Verifier()
2372 v.satisfy_general(verifying_function)
2373 v.verify(root_macaroon, key, [discharge_macaroon])
2374
2375 def test_multiple_mixed(self):
2376 bad_macaroon, _ = self.build_macaroon(service_location="other service")
2377 data = dict(email='foo@bar.com', password='foobar123', macaroons=[
2378 ('good', self.root_macaroon.serialize()),
2379 ('bad', bad_macaroon.serialize()),
2380 ])
2381 self.assert_failed_login('INVALID_CREDENTIALS', data,
2382 expected_status_code=401,
2383 check_login_failed=False)
2384
23472385
2348class MacaroonHandlerTimelineTestCase(MacaroonHandlerBaseTestCase,2386class MacaroonHandlerTimelineTestCase(MacaroonHandlerBaseTestCase,
2349 TimelineActionMixin):2387 TimelineActionMixin):