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
1=== modified file 'src/api/v20/handlers.py'
2--- src/api/v20/handlers.py 2016-03-22 21:06:07 +0000
3+++ src/api/v20/handlers.py 2016-04-18 16:05:29 +0000
4@@ -438,12 +438,21 @@
5 try:
6 email = data['email']
7 password = data['password']
8- root_macaroon_raw = data['macaroon']
9 except KeyError:
10- expected = set(('email', 'password', 'macaroon'))
11+ expected = {'email', 'password'}
12 missing = dict((k, [FIELD_REQUIRED]) for k in expected - set(data))
13 return errors.INVALID_DATA(**missing)
14
15+ if 'macaroon' in data:
16+ simple_macaroon = True
17+ root_macaroons_info = [(None, data['macaroon'])]
18+ elif 'macaroons' in data:
19+ simple_macaroon = False
20+ root_macaroons_info = data['macaroons']
21+ else:
22+ missing = {'macaroon or macaroons': [FIELD_REQUIRED]}
23+ return errors.INVALID_DATA(**missing)
24+
25 account = None
26 response = None
27 try:
28@@ -481,16 +490,22 @@
29 if response is not None:
30 return response
31
32- try:
33- discharge = auth.build_discharge_macaroon(
34- account, root_macaroon_raw)
35- except ValidationError:
36- return errors.INVALID_DATA()
37- except AuthenticationError:
38- return errors.INVALID_CREDENTIALS()
39+ all_discharges = []
40+ for mac_id, root_macaroon_raw in root_macaroons_info:
41+ try:
42+ discharge = auth.build_discharge_macaroon(
43+ account, root_macaroon_raw)
44+ except ValidationError:
45+ return errors.INVALID_DATA()
46+ except AuthenticationError:
47+ return errors.INVALID_CREDENTIALS()
48+ all_discharges.append((mac_id, discharge.serialize()))
49
50 response = rc.ALL_OK
51- response.content = dict(discharge_macaroon=discharge.serialize())
52+ if simple_macaroon:
53+ response.content = dict(discharge_macaroon=all_discharges[0][1])
54+ else:
55+ response.content = dict(discharge_macaroons=all_discharges)
56
57 log_type = AuthLogType.API_MACAROON_DISCHARGE_NEW
58 login_succeeded.send(sender=self, user=account, request=request,
59
60=== modified file 'src/api/v20/tests/test_handlers.py'
61--- src/api/v20/tests/test_handlers.py 2016-04-01 19:35:14 +0000
62+++ src/api/v20/tests/test_handlers.py 2016-04-18 16:05:29 +0000
63@@ -2344,6 +2344,44 @@
64 expected_status_code=401,
65 check_login_failed=False)
66
67+ def test_missing_macaroons(self):
68+ data = dict(email='foo@bar.com', password='foobar123')
69+ extra = {'macaroon or macaroons': [handlers.FIELD_REQUIRED]}
70+ self.assert_failed_login('INVALID_DATA', data, extra=extra,
71+ expected_status_code=400,
72+ check_login_failed=False)
73+
74+ def test_multiple_macaroons(self):
75+ # build *several* macaroons and send them all at once
76+ roots, rkeys = zip(*[self.build_macaroon() for _ in range(3)])
77+ ids = range(3)
78+ roots = dict(zip(ids, roots))
79+ rkeys = dict(zip(ids, rkeys))
80+ macaroons = [(mac_id, m.serialize()) for mac_id, m in roots.items()]
81+ data = dict(email='foo@bar.com', password='foobar123',
82+ macaroons=macaroons)
83+ json_body = self.do_post(data=data)
84+
85+ # get all the discharges and verify their integrity
86+ verifying_function = lambda c: True
87+ for mac_id, discharge_macaroon_raw in json_body['discharge_macaroons']:
88+ discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
89+ root_macaroon = roots[mac_id]
90+ key = rkeys[mac_id]
91+ v = Verifier()
92+ v.satisfy_general(verifying_function)
93+ v.verify(root_macaroon, key, [discharge_macaroon])
94+
95+ def test_multiple_mixed(self):
96+ bad_macaroon, _ = self.build_macaroon(service_location="other service")
97+ data = dict(email='foo@bar.com', password='foobar123', macaroons=[
98+ ('good', self.root_macaroon.serialize()),
99+ ('bad', bad_macaroon.serialize()),
100+ ])
101+ self.assert_failed_login('INVALID_CREDENTIALS', data,
102+ expected_status_code=401,
103+ check_login_failed=False)
104+
105
106 class MacaroonHandlerTimelineTestCase(MacaroonHandlerBaseTestCase,
107 TimelineActionMixin):