Merge lp:~facundo/canonical-identity-provider/new-era-discharge into lp:canonical-identity-provider/release

Proposed by Facundo Batista
Status: Merged
Merged at revision: 1440
Proposed branch: lp:~facundo/canonical-identity-provider/new-era-discharge
Merge into: lp:canonical-identity-provider/release
Diff against target: 952 lines (+461/-107)
9 files modified
src/api/v20/handlers.py (+41/-16)
src/api/v20/tests/test_handlers.py (+103/-18)
src/identityprovider/auth.py (+84/-21)
src/identityprovider/tests/test_auth.py (+219/-38)
src/identityprovider/tests/test_forms.py (+6/-6)
src/identityprovider/tests/test_macaroon.py (+1/-1)
src/identityprovider/tests/test_views_server.py (+4/-4)
src/identityprovider/tests/utils.py (+1/-1)
src/identityprovider/views/server.py (+2/-2)
To merge this branch: bzr merge lp:~facundo/canonical-identity-provider/new-era-discharge
Reviewer Review Type Date Requested Status
William Grant Needs Fixing
Facundo Batista (community) Approve
Ricardo Kirkner (community) Approve
Review via email: mp+293879@code.launchpad.net

Commit message

Accept the caveat_id (instead of macaroon) and return the unbound discharge (instead of binding it) for both create and refresh discharges.

Description of the change

Accept the caveat_id (instead of macaroon) and return the unbound discharge (instead of binding it) for both create and refresh discharges.

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

LGTM

review: Approve
Revision history for this message
Facundo Batista (facundo) :
review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (33.8 KiB)

The attempt to merge lp:~facundo/canonical-identity-provider/new-era-discharge into lp:canonical-identity-provider failed. Below is the output from the failed tests.

Bootstrapping...
rm -rf /mnt/tarmac/cache/canonical-identity-provider/merges/trunk/env
rm -rf branches/wheels
rm -rf branches/*
rm -rf staticfiles
rm -f lib/versioninfo.py
find -name '*.pyc' -delete
find -name '*.~*' -delete
Not deleting /mnt/tarmac/cache/canonical-identity-provider/merges/trunk/env/bin
New python executable in /mnt/tarmac/cache/canonical-identity-provider/merges/trunk/env/bin/python
Installing setuptools, pip...done.
/usr/lib/config-manager/cm.py update config-manager.txt
touch branches/last_build
[ -d branches/wheels ] && (cd branches/wheels && bzr pull) || (bzr branch lp:~ubuntuone-pqm-team/canonical-identity-provider/dependencies branches/wheels)
bzr version-info --format=python > lib/versioninfo.py
/mnt/tarmac/cache/canonical-identity-provider/merges/trunk/env/bin/python /mnt/tarmac/cache/canonical-identity-provider/merges/trunk/env/bin/pip install --find-links=branches/wheels --no-index -r requirements.txt
Ignoring indexes: https://pypi.python.org/simple/
Downloading/unpacking bson==0.3.3 (from -r requirements.txt (line 1))
Downloading/unpacking canonical-raven==0.0.3 (from -r requirements.txt (line 2))
Downloading/unpacking convoy==0.4.1 (from -r requirements.txt (line 3))
Downloading/unpacking django==1.8.13 (from -r requirements.txt (line 4))
Downloading/unpacking django-honeypot==0.4.0 (from -r requirements.txt (line 5))
Downloading/unpacking django-jsonfield==0.9.13 (from -r requirements.txt (line 6))
Downloading/unpacking django-model-utils==2.2 (from -r requirements.txt (line 7))
Downloading/unpacking django-modeldict==1.4.1 (from -r requirements.txt (line 8))
Downloading/unpacking django-preflight==0.1.5 (from -r requirements.txt (line 9))
Downloading/unpacking django-secure==1.0.1 (from -r requirements.txt (line 10))
Downloading/unpacking django-statsd-mozilla==0.3.15 (from -r requirements.txt (line 11))
Downloading/unpacking gargoyle==0.11.0 (from -r requirements.txt (line 12))
Downloading/unpacking gunicorn==19.3.0 (from -r requirements.txt (line 13))
Downloading/unpacking lazr.authentication==0.1.3 (from -r requirements.txt (line 14))
Downloading/unpacking libnacl==1.3.6 (from -r requirements.txt (line 15))
Downloading/unpacking nexus==0.3.1 (from -r requirements.txt (line 16))
Downloading/unpacking oath==1.4.0 (from -r requirements.txt (line 17))
Downloading/unpacking oauthlib==0.7.2 (from -r requirements.txt (line 18))
Requirement already satisfied (use --upgrade to upgrade): oops==0.0.13 in /usr/lib/pymodules/python2.7 (from -r requirements.txt (line 19))
Downloading/unpacking oops-amqp==0.0.8b1 (from -r requirements.txt (line 20))
Downloading/unpacking oops-datedir-repo==0.0.23 (from -r requirements.txt (line 21))
Downloading/unpacking oops-dictconfig==0.0.6 (from -r requirements.txt (line 22))
Downloading/unpacking oops-timeline==0.0.2 (from -r requirements.txt (line 23))
Downloading/unpacking oops-wsgi==0.0.11 (from -r requirements.txt (line 24))
Downloading/unpacking paste==2.0.1 (from -r requirements.txt (line 25))
Requirement already s...

Revision history for this message
Facundo Batista (facundo) wrote :

Already fixed

review: Approve
Revision history for this message
William Grant (wgrant) :
review: Needs Fixing
Revision history for this message
Facundo Batista (facundo) :
Revision history for this message
William Grant (wgrant) :
Revision history for this message
Facundo Batista (facundo) wrote :

Addressed the "no caveat id in refreshing" issue

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-04-18 15:53:18 +0000
+++ src/api/v20/handlers.py 2016-05-05 12:45:29 +0000
@@ -58,6 +58,7 @@
58 TokenScope,58 TokenScope,
59)59)
60from identityprovider.signals import login_failed, login_succeeded60from identityprovider.signals import login_failed, login_succeeded
61from identityprovider.stats import stats
61from identityprovider.timeline_helpers import get_request_timing_function62from identityprovider.timeline_helpers import get_request_timing_function
62from identityprovider.utils import redirection_url_for_token63from identityprovider.utils import redirection_url_for_token
6364
@@ -446,11 +447,19 @@
446 if 'macaroon' in data:447 if 'macaroon' in data:
447 simple_macaroon = True448 simple_macaroon = True
448 root_macaroons_info = [(None, data['macaroon'])]449 root_macaroons_info = [(None, data['macaroon'])]
450 stats.increment('macaroon.discharge', key='single_macaroon')
449 elif 'macaroons' in data:451 elif 'macaroons' in data:
450 simple_macaroon = False452 simple_macaroon = False
451 root_macaroons_info = data['macaroons']453 root_macaroons_info = data['macaroons']
454 stats.increment('macaroon.discharge', key='multi_macaroons')
455 elif 'caveat_id' in data:
456 simple_macaroon = True
457 root_macaroons_info = None
458 caveat_id = data['caveat_id']
459 stats.increment('macaroon.discharge', key='caveat_id')
452 else:460 else:
453 missing = {'macaroon or macaroons': [FIELD_REQUIRED]}461 # let's flag the new one only, not the deprecated ones
462 missing = {'caveat_id': [FIELD_REQUIRED]}
454 return errors.INVALID_DATA(**missing)463 return errors.INVALID_DATA(**missing)
455464
456 account = None465 account = None
@@ -490,16 +499,22 @@
490 if response is not None:499 if response is not None:
491 return response500 return response
492501
493 all_discharges = []502 try:
494 for mac_id, root_macaroon_raw in root_macaroons_info:503 if root_macaroons_info is None:
495 try:504 discharge = auth.build_discharge_macaroon(account, caveat_id)
496 discharge = auth.build_discharge_macaroon(505 all_discharges = [(None, discharge.serialize())]
497 account, root_macaroon_raw)506 else:
498 except ValidationError:507 # 2016-05-03: this is deprecated, just supporting it until
499 return errors.INVALID_DATA()508 # no more clients send root macaroon(s)
500 except AuthenticationError:509 all_discharges = []
501 return errors.INVALID_CREDENTIALS()510 for mac_id, root_macaroon_raw in root_macaroons_info:
502 all_discharges.append((mac_id, discharge.serialize()))511 discharge = auth.build_discharge_macaroon_from_root(
512 account, root_macaroon_raw)
513 all_discharges.append((mac_id, discharge.serialize()))
514 except ValidationError:
515 return errors.INVALID_DATA()
516 except AuthenticationError:
517 return errors.INVALID_CREDENTIALS()
503518
504 response = rc.ALL_OK519 response = rc.ALL_OK
505 if simple_macaroon:520 if simple_macaroon:
@@ -523,16 +538,26 @@
523538
524 data = request.data539 data = request.data
525 try:540 try:
526 root_macaroon_raw = data['root_macaroon']
527 discharge_macaroon_raw = data['discharge_macaroon']541 discharge_macaroon_raw = data['discharge_macaroon']
528 except KeyError:542 except KeyError:
529 expected = {'root_macaroon', 'discharge_macaroon'}543 missing = {'discharge_macaroon': [FIELD_REQUIRED]}
530 missing = dict((k, [FIELD_REQUIRED]) for k in expected - set(data))544 return errors.INVALID_DATA(**missing)
545
546 if 'caveat_id' in data:
547 method = auth.refresh_discharge
548 params = (data['caveat_id'], discharge_macaroon_raw)
549 stats.increment('macaroon.refresh', key='caveat_id')
550 elif 'root_macaroon' in data:
551 method = auth.refresh_macaroons
552 params = (data['root_macaroon'], discharge_macaroon_raw)
553 stats.increment('macaroon.refresh', key='macaroon')
554 else:
555 # let's flag the new one only, not the deprecated one
556 missing = {'caveat_id': [FIELD_REQUIRED]}
531 return errors.INVALID_DATA(**missing)557 return errors.INVALID_DATA(**missing)
532558
533 try:559 try:
534 new_discharge = auth.refresh_macaroons(root_macaroon_raw,560 new_discharge = method(*params)
535 discharge_macaroon_raw)
536 except ValidationError:561 except ValidationError:
537 return errors.INVALID_DATA()562 return errors.INVALID_DATA()
538 except AccountDeactivated:563 except AccountDeactivated:
539564
=== modified file 'src/api/v20/tests/test_handlers.py'
--- src/api/v20/tests/test_handlers.py 2016-04-18 15:53:18 +0000
+++ src/api/v20/tests/test_handlers.py 2016-05-05 12:45:29 +0000
@@ -2169,13 +2169,25 @@
2169 self.login_failed_calls = []2169 self.login_failed_calls = []
2170 login_failed.connect(self.track_failed_logins, dispatch_uid=self.id())2170 login_failed.connect(self.track_failed_logins, dispatch_uid=self.id())
21712171
2172 self.root_macaroon, self.macaroon_random_key = self.build_macaroon()2172 self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon()
2173 self.data = dict(2173 self.data = dict(
2174 email='foo@bar.com', password='foobar123',2174 email='foo@bar.com', password='foobar123',
2175 macaroon=self.root_macaroon.serialize())2175 macaroon=self.root_macaroon.serialize())
2176 self.account = self.factory.make_account(2176 self.account = self.factory.make_account(
2177 email=self.data['email'], password=self.data['password'])2177 email=self.data['email'], password=self.data['password'])
21782178
2179 self._stats_increment = self.patch('api.v20.handlers.stats.increment')
2180
2181 def check_stats_increment(self, *args, **kwargs):
2182 """Check stats increment was properly called."""
2183 calls = self._stats_increment.call_args_list
2184 self.assertEqual(len(calls), 1)
2185 call_args, call_kwargs = calls[0]
2186 self.assertEqual(call_args, args,
2187 "Bad args in stats increment call {}".format(calls))
2188 self.assertEqual(call_kwargs, kwargs,
2189 "Bad kwargs in stats increment call {}".format(calls))
2190
21792191
2180class MacaroonDischargeHandlerTestCase(MacaroonHandlerBaseTestCase,2192class MacaroonDischargeHandlerTestCase(MacaroonHandlerBaseTestCase,
2181 AuthLogTestCaseMixin):2193 AuthLogTestCaseMixin):
@@ -2199,7 +2211,7 @@
2199 response)2211 response)
2200 return json.loads(response.content)2212 return json.loads(response.content)
22012213
2202 def assert_response_correct(self, json_body, account):2214 def assert_response_correct(self, json_body, account, bind=False):
2203 """Check we received a good discharge macaroon for the root sent.2215 """Check we received a good discharge macaroon for the root sent.
22042216
2205 Proper verification of the received macaroon internals happens in2217 Proper verification of the received macaroon internals happens in
@@ -2207,6 +2219,9 @@
2207 """2219 """
2208 discharge_macaroon_raw = json_body['discharge_macaroon']2220 discharge_macaroon_raw = json_body['discharge_macaroon']
2209 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)2221 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
2222 if bind:
2223 discharge_macaroon = self.root_macaroon.prepare_for_request(
2224 discharge_macaroon)
2210 v = Verifier()2225 v = Verifier()
2211 v.satisfy_general(lambda c: True)2226 v.satisfy_general(lambda c: True)
2212 v.verify(2227 v.verify(
@@ -2327,6 +2342,7 @@
2327 def test_macaroon_created(self):2342 def test_macaroon_created(self):
2328 json_body = self.do_post()2343 json_body = self.do_post()
2329 self.assert_response_correct(json_body, self.account)2344 self.assert_response_correct(json_body, self.account)
2345 self.check_stats_increment('macaroon.discharge', key='single_macaroon')
23302346
2331 def test_root_macaroon_corrupt(self):2347 def test_root_macaroon_corrupt(self):
2332 data = dict(2348 data = dict(
@@ -2337,23 +2353,23 @@
2337 check_login_failed=False)2353 check_login_failed=False)
23382354
2339 def test_root_macaroon_not_for_sso(self):2355 def test_root_macaroon_not_for_sso(self):
2340 macaroon, _ = self.build_macaroon(service_location="other service")2356 macaroon, _, _ = self.build_macaroon(service_location="other service")
2341 data = dict(email='foo@bar.com', password='foobar123',2357 data = dict(email='foo@bar.com', password='foobar123',
2342 macaroon=macaroon.serialize())2358 macaroon=macaroon.serialize())
2343 self.assert_failed_login('INVALID_CREDENTIALS', data,2359 self.assert_failed_login('INVALID_CREDENTIALS', data,
2344 expected_status_code=401,2360 expected_status_code=401,
2345 check_login_failed=False)2361 check_login_failed=False)
23462362
2347 def test_missing_macaroons(self):2363 def test_missing_macaroon_info(self):
2348 data = dict(email='foo@bar.com', password='foobar123')2364 data = dict(email='foo@bar.com', password='foobar123')
2349 extra = {'macaroon or macaroons': [handlers.FIELD_REQUIRED]}2365 extra = {'caveat_id': [handlers.FIELD_REQUIRED]}
2350 self.assert_failed_login('INVALID_DATA', data, extra=extra,2366 self.assert_failed_login('INVALID_DATA', data, extra=extra,
2351 expected_status_code=400,2367 expected_status_code=400,
2352 check_login_failed=False)2368 check_login_failed=False)
23532369
2354 def test_multiple_macaroons(self):2370 def test_multiple_macaroons(self):
2355 # build *several* macaroons and send them all at once2371 # build *several* macaroons and send them all at once
2356 roots, rkeys = zip(*[self.build_macaroon() for _ in range(3)])2372 roots, rkeys, _ = zip(*[self.build_macaroon() for _ in range(3)])
2357 ids = range(3)2373 ids = range(3)
2358 roots = dict(zip(ids, roots))2374 roots = dict(zip(ids, roots))
2359 rkeys = dict(zip(ids, rkeys))2375 rkeys = dict(zip(ids, rkeys))
@@ -2372,8 +2388,11 @@
2372 v.satisfy_general(verifying_function)2388 v.satisfy_general(verifying_function)
2373 v.verify(root_macaroon, key, [discharge_macaroon])2389 v.verify(root_macaroon, key, [discharge_macaroon])
23742390
2391 self.check_stats_increment('macaroon.discharge', key='multi_macaroons')
2392
2375 def test_multiple_mixed(self):2393 def test_multiple_mixed(self):
2376 bad_macaroon, _ = self.build_macaroon(service_location="other service")2394 bad_macaroon, _, _ = self.build_macaroon(
2395 service_location="other service")
2377 data = dict(email='foo@bar.com', password='foobar123', macaroons=[2396 data = dict(email='foo@bar.com', password='foobar123', macaroons=[
2378 ('good', self.root_macaroon.serialize()),2397 ('good', self.root_macaroon.serialize()),
2379 ('bad', bad_macaroon.serialize()),2398 ('bad', bad_macaroon.serialize()),
@@ -2382,6 +2401,24 @@
2382 expected_status_code=401,2401 expected_status_code=401,
2383 check_login_failed=False)2402 check_login_failed=False)
23842403
2404 def test_caveat_id_discharge_created(self):
2405 # discharge the test macaroon
2406 (caveat,) = [c for c in self.root_macaroon.third_party_caveats()
2407 if c.location == settings.MACAROON_SERVICE_LOCATION]
2408 data = dict(email='foo@bar.com', password='foobar123',
2409 caveat_id=caveat.caveat_id)
2410
2411 json_body = self.do_post(data=data)
2412 self.assert_response_correct(json_body, self.account, bind=True)
2413 self.check_stats_increment('macaroon.discharge', key='caveat_id')
2414
2415 def test_caveat_id_corrupt(self):
2416 data = dict(email='foo@bar.com', password='foobar123',
2417 caveat_id="I'm a seriously corrupted caveat")
2418 self.assert_failed_login('INVALID_DATA', data,
2419 expected_status_code=400,
2420 check_login_failed=False)
2421
23852422
2386class MacaroonHandlerTimelineTestCase(MacaroonHandlerBaseTestCase,2423class MacaroonHandlerTimelineTestCase(MacaroonHandlerBaseTestCase,
2387 TimelineActionMixin):2424 TimelineActionMixin):
@@ -2428,12 +2465,16 @@
2428 super(MacaroonRefreshHandlerTestCase, self).setUp()2465 super(MacaroonRefreshHandlerTestCase, self).setUp()
24292466
2430 # discharge the test macaroon2467 # discharge the test macaroon
2431 root_macaroon_raw = self.root_macaroon.serialize()2468 (caveat,) = [c for c in self.root_macaroon.third_party_caveats()
2432 self.discharge_macaroon = build_discharge_macaroon(2469 if c.location == settings.MACAROON_SERVICE_LOCATION]
2433 self.account, root_macaroon_raw)2470
24342471 unbound_discharge = build_discharge_macaroon(
2435 self.data = {'root_macaroon': root_macaroon_raw,2472 self.account, caveat.caveat_id)
2436 'discharge_macaroon': self.discharge_macaroon.serialize()}2473 self.discharge_macaroon = self.root_macaroon.prepare_for_request(
2474 unbound_discharge)
2475
2476 self.data = {'caveat_id': caveat.caveat_id,
2477 'discharge_macaroon': unbound_discharge.serialize()}
24372478
2438 def do_post(self, data=None, expected_status_code=200,2479 def do_post(self, data=None, expected_status_code=200,
2439 content_type='application/json', **kwargs):2480 content_type='application/json', **kwargs):
@@ -2463,13 +2504,23 @@
2463 self.assertEqual(json_body['extra'], extra)2504 self.assertEqual(json_body['extra'], extra)
2464 return json_body2505 return json_body
24652506
2466 def test_required_parameters(self):2507 def test_required_parameters_discharge(self):
2467 json_body = self.do_post(expected_status_code=400, data={})2508 json_body = self.do_post(expected_status_code=400, data={})
24682509
2469 self.assertEqual(json_body, {2510 self.assertEqual(json_body, {
2470 'code': 'INVALID_DATA',2511 'code': 'INVALID_DATA',
2471 'extra': {'root_macaroon': [handlers.FIELD_REQUIRED],2512 'extra': {'discharge_macaroon': [handlers.FIELD_REQUIRED]},
2472 'discharge_macaroon': [handlers.FIELD_REQUIRED]},2513 'message': 'Invalid request data'},
2514 )
2515 self.assertEqual(self.login_failed_calls, [])
2516
2517 def test_required_parameters_caveat_id(self):
2518 json_body = self.do_post(expected_status_code=400,
2519 data={'discharge_macaroon': 'whatever'})
2520
2521 self.assertEqual(json_body, {
2522 'code': 'INVALID_DATA',
2523 'extra': {'caveat_id': [handlers.FIELD_REQUIRED]},
2473 'message': 'Invalid request data'},2524 'message': 'Invalid request data'},
2474 )2525 )
2475 self.assertEqual(self.login_failed_calls, [])2526 self.assertEqual(self.login_failed_calls, [])
@@ -2486,7 +2537,7 @@
2486 check_login_failed=False)2537 check_login_failed=False)
24872538
2488 def test_macaroon_bad_authinfo(self):2539 def test_macaroon_bad_authinfo(self):
2489 macaroon, _ = self.build_macaroon(service_location="other service")2540 macaroon, _, _ = self.build_macaroon(service_location="other service")
2490 data = dict(root_macaroon=macaroon.serialize(),2541 data = dict(root_macaroon=macaroon.serialize(),
2491 discharge_macaroon=self.discharge_macaroon.serialize())2542 discharge_macaroon=self.discharge_macaroon.serialize())
2492 self.assert_failed_login('INVALID_CREDENTIALS', data,2543 self.assert_failed_login('INVALID_CREDENTIALS', data,
@@ -2494,7 +2545,9 @@
2494 check_login_failed=False)2545 check_login_failed=False)
24952546
2496 def test_macaroon_refreshed(self):2547 def test_macaroon_refreshed(self):
2497 json_body = self.do_post()2548 data = {'root_macaroon': self.root_macaroon.serialize(),
2549 'discharge_macaroon': self.discharge_macaroon.serialize()}
2550 json_body = self.do_post(data=data)
24982551
2499 # get new discharge macaroon and verify with old root (its internals2552 # get new discharge macaroon and verify with old root (its internals
2500 # are verified by the tests of the 'refresh_macaroons' function itself)2553 # are verified by the tests of the 'refresh_macaroons' function itself)
@@ -2504,3 +2557,35 @@
2504 v.satisfy_general(lambda c: True)2557 v.satisfy_general(lambda c: True)
2505 v.verify(2558 v.verify(
2506 self.root_macaroon, self.macaroon_random_key, [discharge_macaroon])2559 self.root_macaroon, self.macaroon_random_key, [discharge_macaroon])
2560 self.check_stats_increment('macaroon.refresh', key='macaroon')
2561
2562 def test_caveat_id_corrupt(self):
2563 data = dict(caveat_id="I'm a seriously corrupted caveat",
2564 discharge_macaroon="Also broken")
2565 self.assert_failed_login('INVALID_DATA', data,
2566 expected_status_code=400,
2567 check_login_failed=False)
2568
2569 def test_caveat_id_bad_authinfo(self):
2570 macaroon, _, _ = self.build_macaroon(service_location="other service")
2571 (caveat,) = [c for c in macaroon.third_party_caveats()
2572 if c.location == "other service"]
2573 data = dict(caveat_id=caveat.caveat_id,
2574 discharge_macaroon=self.discharge_macaroon.serialize())
2575 self.assert_failed_login('INVALID_CREDENTIALS', data,
2576 expected_status_code=401,
2577 check_login_failed=False)
2578
2579 def test_caveat_id_refreshed(self):
2580 json_body = self.do_post()
2581
2582 # get new discharge macaroon, apply it and verify with old root (its
2583 # internals are verified by the tests of the 'refresh_macaroons'
2584 # function itself)
2585 unbound_discharge_raw = json_body['discharge_macaroon']
2586 unbound_discharge = Macaroon.deserialize(unbound_discharge_raw)
2587 discharge = self.root_macaroon.prepare_for_request(unbound_discharge)
2588 v = Verifier()
2589 v.satisfy_general(lambda c: True)
2590 v.verify(self.root_macaroon, self.macaroon_random_key, [discharge])
2591 self.check_stats_increment('macaroon.refresh', key='caveat_id')
25072592
=== modified file 'src/identityprovider/auth.py'
--- src/identityprovider/auth.py 2016-03-23 16:20:24 +0000
+++ src/identityprovider/auth.py 2016-05-05 12:45:29 +0000
@@ -507,16 +507,21 @@
507 # the macaroon doesn't have a location for this service507 # the macaroon doesn't have a location for this service
508 raise AuthenticationError("The received macaroon is not for ours")508 raise AuthenticationError("The received macaroon is not for ours")
509509
510 # get the only-for-this-project random key510 caveat_info = _decrypt_caveat(sso_caveat.caveat_id)
511 return sso_caveat.caveat_id, caveat_info
512
513
514def _decrypt_caveat(raw_caveat_id):
515 """Decrypt and decode the caveat id info."""
511 try:516 try:
512 caveat_info_raw = settings.CRYPTO_SSO_PRIVKEY.decrypt(517 caveat_info_raw = settings.CRYPTO_SSO_PRIVKEY.decrypt(
513 base64.b64decode(sso_caveat.caveat_id))518 base64.b64decode(raw_caveat_id))
514 caveat_info = json.loads(caveat_info_raw)519 caveat_info = json.loads(caveat_info_raw)
515 except:520 except:
516 # not properly encrypted information inside521 # not properly encrypted information inside
517 raise AuthenticationError("Bad info in the caveat_id")522 raise ValidationError("Bad info in the caveat_id")
518523
519 return sso_caveat.caveat_id, caveat_info524 return caveat_info
520525
521526
522def _build_discharge(macaroon_key, caveat_id, account,527def _build_discharge(macaroon_key, caveat_id, account,
@@ -556,8 +561,12 @@
556 return d561 return d
557562
558563
559def build_discharge_macaroon(account, root_macaroon_raw):564def build_discharge_macaroon_from_root(account, root_macaroon_raw):
560 """Build a discharge macaroon from a root one."""565 """Build a discharge macaroon from a root one.
566
567 This function is deprecated; will be removed when clients stop hitting
568 the handler passing the root macaroon.
569 """
561 try:570 try:
562 root_macaroon = Macaroon.deserialize(root_macaroon_raw)571 root_macaroon = Macaroon.deserialize(root_macaroon_raw)
563 except:572 except:
@@ -576,18 +585,24 @@
576 return discharge585 return discharge
577586
578587
579def refresh_macaroons(root_macaroon_raw, discharge_macaroon_raw):588def build_discharge_macaroon(account, raw_caveat_id):
580 """Refresh a root/discharge pair with a new discharge macaroon."""589 """Build a discharge macaroon from the caveat id."""
590 # get the decrypted deserialized info
591 caveat_info = _decrypt_caveat(raw_caveat_id)
592
593 # create a discharge macaroon with same location, key and
594 # identifier than it's original 3rd-party caveat (so they can
595 # be matched and verified)
596 discharge = _build_discharge(
597 caveat_info['3rdparty'], raw_caveat_id, account)
598
599 # return the unbound discharge macaroon
600 return discharge
601
602
603def _account_from_macaroon(macaroon, key, discharge=None):
604 """Get the new discharge macaroon."""
581 service_location = settings.MACAROON_SERVICE_LOCATION605 service_location = settings.MACAROON_SERVICE_LOCATION
582
583 try:
584 root_macaroon = Macaroon.deserialize(root_macaroon_raw)
585 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
586 except:
587 raise ValidationError("The received Macaroons are corrupt")
588
589 # get the raw caveat id and the decrypted deserialized info
590 raw_caveat_id, caveat_info = _get_own_caveat(root_macaroon)
591 info_holder = {}606 info_holder = {}
592607
593 def checker(caveat):608 def checker(caveat):
@@ -606,7 +621,10 @@
606 v = Verifier()621 v = Verifier()
607 v.satisfy_general(checker)622 v.satisfy_general(checker)
608 try:623 try:
609 v.verify(root_macaroon, caveat_info['roothash'], [discharge_macaroon])624 if discharge is None:
625 v.verify(macaroon, key)
626 else:
627 v.verify(macaroon, key, [discharge])
610 except:628 except:
611 raise AuthenticationError("Not verifying macaroons")629 raise AuthenticationError("Not verifying macaroons")
612630
@@ -620,14 +638,59 @@
620 if account.accountpassword.date_changed > last_auth_ts:638 if account.accountpassword.date_changed > last_auth_ts:
621 raise AuthenticationError("Password changed")639 raise AuthenticationError("Password changed")
622640
641 return account, info_holder
642
643
644def refresh_macaroons(root_macaroon_raw, discharge_macaroon_raw):
645 """Refresh a root/discharge pair with a new discharge macaroon.
646
647 This function is deprecated; will be removed when clients stop hitting
648 the handler passing the root macaroon.
649 """
650 try:
651 root_macaroon = Macaroon.deserialize(root_macaroon_raw)
652 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
653 except:
654 raise ValidationError("The received Macaroons are corrupt")
655
656 # get the raw caveat id and the decrypted deserialized info
657 raw_caveat_id, caveat_info = _get_own_caveat(root_macaroon)
658
659 account, macaroon_info = _account_from_macaroon(
660 root_macaroon, caveat_info['roothash'], discharge_macaroon)
661
623 # create the new discharge macaroon with same location, key and662 # create the new discharge macaroon with same location, key and
624 # identifier than it's original 3rd-party caveat (so they can663 # identifier than it's original 3rd-party caveat (so they can
625 # be matched and verified), and keeping the last_auth and expires from664 # be matched and verified), and keeping the last_auth and expires from
626 # the original discharge macaroon665 # the original discharge macaroon
627 d = _build_discharge(caveat_info['3rdparty'], raw_caveat_id, account,666 d = _build_discharge(
628 last_auth=info_holder['last_auth'],667 caveat_info['3rdparty'], raw_caveat_id, account,
629 expires=info_holder['expires'])668 last_auth=macaroon_info['last_auth'], expires=macaroon_info['expires'])
630669
631 # return the properly prepared discharge macaroon670 # return the properly prepared discharge macaroon
632 discharge = root_macaroon.prepare_for_request(d)671 discharge = root_macaroon.prepare_for_request(d)
633 return discharge672 return discharge
673
674
675def refresh_discharge(raw_caveat_id, discharge_macaroon_raw):
676 """Refresh a discharge with a new discharge macaroon."""
677 try:
678 discharge_macaroon = Macaroon.deserialize(discharge_macaroon_raw)
679 except:
680 raise ValidationError("The received Macaroons are corrupt")
681
682 # get the decrypted deserialized info
683 caveat_info = _decrypt_caveat(raw_caveat_id)
684 account, macaroon_info = _account_from_macaroon(
685 discharge_macaroon, caveat_info['3rdparty'])
686
687 # create the new discharge macaroon with same location, key and
688 # identifier than it's original 3rd-party caveat (so they can
689 # be matched and verified), and keeping the last_auth and expires from
690 # the original discharge macaroon
691 discharge = _build_discharge(
692 caveat_info['3rdparty'], raw_caveat_id, account,
693 last_auth=macaroon_info['last_auth'], expires=macaroon_info['expires'])
694
695 # return the unbound discharge macaroon
696 return discharge
634697
=== modified file 'src/identityprovider/tests/test_auth.py'
--- src/identityprovider/tests/test_auth.py 2016-04-01 19:35:14 +0000
+++ src/identityprovider/tests/test_auth.py 2016-05-05 12:45:29 +0000
@@ -29,9 +29,11 @@
29 LaunchpadBackend,29 LaunchpadBackend,
30 SSOOAuthAuthentication,30 SSOOAuthAuthentication,
31 SSORequestValidator,31 SSORequestValidator,
32 _get_own_caveat,32 _decrypt_caveat,
33 basic_authenticate,33 basic_authenticate,
34 build_discharge_macaroon,34 build_discharge_macaroon,
35 build_discharge_macaroon_from_root,
36 refresh_discharge,
35 refresh_macaroons,37 refresh_macaroons,
36 validate_oauth_signature,38 validate_oauth_signature,
37)39)
@@ -958,26 +960,31 @@
958 timer=self.dummy_timer)960 timer=self.dummy_timer)
959961
960962
961class BuildMacaroonDischargeTestCase(SSOBaseTestCase):963class BuildMacaroonFromRootDischargeTestCase(SSOBaseTestCase):
964 """Test the deprecated build_discharge_macaroon_from_root.
965
966 These tests are kept separated as that function won't evolve no more.
967 """
962968
963 def setUp(self):969 def setUp(self):
964 super(BuildMacaroonDischargeTestCase, self).setUp()970 super(BuildMacaroonFromRootDischargeTestCase, self).setUp()
965 self.root_macaroon, self.macaroon_random_key = self.build_macaroon()971 self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon()
966972
967 def test_root_macaroon_corrupt(self):973 def test_root_macaroon_corrupt(self):
968 self.assertRaises(ValidationError, build_discharge_macaroon,974 self.assertRaises(ValidationError, build_discharge_macaroon_from_root,
969 "fake account", "I'm a seriously corrupted macaroon")975 "fake account", "I'm a seriously corrupted macaroon")
970976
971 def test_root_macaroon_not_for_sso(self):977 def test_root_macaroon_not_for_sso(self):
972 macaroon, _ = self.build_macaroon(service_location="other service")978 macaroon, _, _ = self.build_macaroon(service_location="other service")
973 self.assertRaises(AuthenticationError, build_discharge_macaroon,979 self.assertRaises(AuthenticationError,
980 build_discharge_macaroon_from_root,
974 "fake account", macaroon.serialize())981 "fake account", macaroon.serialize())
975982
976 def test_proper_discharging(self):983 def test_proper_discharging(self):
977 # build the input and call984 # build the input and call
978 real_account = self.factory.make_account()985 real_account = self.factory.make_account()
979 before = now()986 before = now()
980 discharge_macaroon = build_discharge_macaroon(987 discharge_macaroon = build_discharge_macaroon_from_root(
981 real_account, self.root_macaroon.serialize())988 real_account, self.root_macaroon.serialize())
982 after = now()989 after = now()
983990
@@ -1029,6 +1036,76 @@
1029 [discharge_macaroon])1036 [discharge_macaroon])
10301037
10311038
1039class BuildMacaroonDischargeTestCase(SSOBaseTestCase):
1040
1041 def test_caveat_id_corrupt(self):
1042 self.assertRaises(ValidationError, build_discharge_macaroon,
1043 "fake account", "I'm a seriously corrupted caveatid")
1044
1045 def test_caveat_id_not_for_sso(self):
1046 macaroon, _, _ = self.build_macaroon(service_location="other service")
1047 (sso_caveat,) = [c for c in macaroon.third_party_caveats()
1048 if c.location == "other service"]
1049
1050 self.assertRaises(ValidationError, build_discharge_macaroon,
1051 "fake account", sso_caveat)
1052
1053 def test_proper_discharging(self):
1054 # build the input and call
1055 root_macaroon, _, random_key = self.build_macaroon()
1056 (sso_caveat,) = [c for c in root_macaroon.third_party_caveats()
1057 if c.location == settings.MACAROON_SERVICE_LOCATION]
1058
1059 real_account = self.factory.make_account()
1060 before = now()
1061 discharge_macaroon = build_discharge_macaroon(
1062 real_account, sso_caveat.caveat_id)
1063 after = now()
1064
1065 # test
1066 def checker(caveat):
1067 """Assure all caveats inside the discharged macaroon are ok."""
1068 source, key, value = caveat.split("|", 2)
1069
1070 if key == 'valid_since':
1071 valid_since = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
1072 self.assertGreater(valid_since, before)
1073 self.assertGreater(after, valid_since)
1074 return True
1075
1076 if key == 'last_auth':
1077 last_auth = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
1078 self.assertGreater(last_auth, before)
1079 self.assertGreater(after, last_auth)
1080 return True
1081
1082 if key == 'account':
1083 acc = json.loads(base64.b64decode(value).decode("utf8"))
1084 self.assertEqual(acc['openid'], real_account.openid_identifier)
1085 self.assertEqual(acc['email'],
1086 real_account.preferredemail.email)
1087 self.assertEqual(acc['displayname'], real_account.displayname)
1088 self.assertEqual(acc['is_verified'], real_account.is_verified)
1089 return True
1090
1091 if key == 'expires':
1092 expires = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
1093 before_plus_ttl = before + timedelta(
1094 seconds=settings.MACAROON_TTL)
1095 after_plus_ttl = after + timedelta(
1096 seconds=settings.MACAROON_TTL)
1097 self.assertGreater(expires, before_plus_ttl)
1098 self.assertGreater(after_plus_ttl, expires)
1099 return True
1100
1101 # we're not validating an SSO from the discharged macaroon, fail!
1102 return False
1103
1104 v = Verifier()
1105 v.satisfy_general(checker)
1106 v.verify(discharge_macaroon, random_key, [])
1107
1108
1032class MacaroonHelpersTestCase(SSOBaseTestCase):1109class MacaroonHelpersTestCase(SSOBaseTestCase):
10331110
1034 def test_get_caveat_ok(self):1111 def test_get_caveat_ok(self):
@@ -1052,42 +1129,29 @@
1052 settings.MACAROON_SERVICE_LOCATION, random_key, info_encrypted)1129 settings.MACAROON_SERVICE_LOCATION, random_key, info_encrypted)
10531130
1054 # check1131 # check
1055 raw_caveat_id, caveat_info = _get_own_caveat(root_macaroon)1132 (sso_caveat,) = [c for c in root_macaroon.third_party_caveats()
1056 self.assertEqual(raw_caveat_id, info_encrypted)1133 if c.location == settings.MACAROON_SERVICE_LOCATION]
1134 caveat_info = _decrypt_caveat(sso_caveat.caveat_id)
1057 self.assertEqual(caveat_info, info)1135 self.assertEqual(caveat_info, info)
10581136
1059 def test_get_caveat_not_for_sso(self):
1060 macaroon, _ = self.build_macaroon(service_location="other service")
1061 self.assertRaises(AuthenticationError, _get_own_caveat, macaroon)
1062
1063 def test_get_caveat_badly_encrypted(self):1137 def test_get_caveat_badly_encrypted(self):
1064 test_rsa_priv_key, test_rsa_pub_key = self.setup_key_pair()1138 self.assertRaises(ValidationError,
10651139 _decrypt_caveat, b"not really well encrypted stuff")
1066 # create a Macaron with the proper third party caveat1140
1067 macaroon_random_key = binascii.hexlify(os.urandom(32))1141
1068 root_macaroon = Macaroon(1142class MacaroonRefreshFromRootTestCase(SSOBaseTestCase):
1069 location='The store ;)',1143 """Test the deprecated refresh_macaroons.
1070 key=macaroon_random_key,1144
1071 identifier='A test macaroon',1145 These tests are kept separated as that function won't evolve no more.
1072 )1146 """
1073 random_key = binascii.hexlify(os.urandom(32))
1074 root_macaroon.add_third_party_caveat(
1075 settings.MACAROON_SERVICE_LOCATION, random_key,
1076 b"not really well encrypted stuff")
1077
1078 # check
1079 self.assertRaises(AuthenticationError, _get_own_caveat, root_macaroon)
1080
1081
1082class MacaroonRefreshTestCase(SSOBaseTestCase):
10831147
1084 def setUp(self):1148 def setUp(self):
1085 super(MacaroonRefreshTestCase, self).setUp()1149 super(MacaroonRefreshFromRootTestCase, self).setUp()
1086 self.root_macaroon, self.macaroon_random_key = self.build_macaroon()1150 self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon()
10871151
1088 # discharge the test macaroon1152 # discharge the test macaroon
1089 self.account = self.factory.make_account()1153 self.account = self.factory.make_account()
1090 self.discharge_macaroon = build_discharge_macaroon(1154 self.discharge_macaroon = build_discharge_macaroon_from_root(
1091 self.account, self.root_macaroon.serialize())1155 self.account, self.root_macaroon.serialize())
10921156
1093 def test_root_macaroon_corrupt(self):1157 def test_root_macaroon_corrupt(self):
@@ -1102,8 +1166,8 @@
11021166
1103 def test_macaroons_dont_verify_ok(self):1167 def test_macaroons_dont_verify_ok(self):
1104 # just get *another* discharge so it's not for the same root macaroon1168 # just get *another* discharge so it's not for the same root macaroon
1105 other_root, _ = self.build_macaroon()1169 other_root, _, _ = self.build_macaroon()
1106 other_discharge = build_discharge_macaroon(1170 other_discharge = build_discharge_macaroon_from_root(
1107 self.account, other_root.serialize())1171 self.account, other_root.serialize())
1108 self.assertRaises(AuthenticationError, refresh_macaroons,1172 self.assertRaises(AuthenticationError, refresh_macaroons,
1109 self.root_macaroon.serialize(),1173 self.root_macaroon.serialize(),
@@ -1197,3 +1261,120 @@
1197 v.satisfy_general(checker)1261 v.satisfy_general(checker)
1198 v.verify(self.root_macaroon, self.macaroon_random_key,1262 v.verify(self.root_macaroon, self.macaroon_random_key,
1199 [new_discharge])1263 [new_discharge])
1264
1265
1266class MacaroonRefreshTestCase(SSOBaseTestCase):
1267
1268 def setUp(self):
1269 super(MacaroonRefreshTestCase, self).setUp()
1270 root_macaroon, _, self.random_key = self.build_macaroon()
1271 (caveat,) = [c for c in root_macaroon.third_party_caveats()
1272 if c.location == settings.MACAROON_SERVICE_LOCATION]
1273 self.caveat_id = caveat.caveat_id
1274
1275 # get a discharge for the test macaroon
1276 self.account = self.factory.make_account()
1277 self.discharge_macaroon = build_discharge_macaroon(
1278 self.account, self.caveat_id)
1279
1280 def test_caveat_id_corrupt(self):
1281 self.assertRaises(
1282 ValidationError, refresh_discharge,
1283 "Corrupted caveat id", self.discharge_macaroon.serialize())
1284
1285 def test_discharge_macaroon_corrupt(self):
1286 self.assertRaises(
1287 ValidationError, refresh_discharge,
1288 self.caveat_id, "Seriously corrupted macaroon")
1289
1290 def test_macaroons_dont_verify_ok(self):
1291 # just get *another* discharge so it's not for the same root macaroon
1292 other_root, _, _ = self.build_macaroon()
1293 (other_caveat,) = [c for c in other_root.third_party_caveats()
1294 if c.location == settings.MACAROON_SERVICE_LOCATION]
1295 other_discharge = build_discharge_macaroon(
1296 self.account, other_caveat.caveat_id)
1297 self.assertRaises(AuthenticationError, refresh_discharge,
1298 self.caveat_id, other_discharge.serialize())
1299
1300 def test_deactivated_account(self):
1301 self.account.deactivate()
1302 self.assertRaises(AccountDeactivated, refresh_discharge,
1303 self.caveat_id, self.discharge_macaroon.serialize())
1304
1305 def test_password_changed(self):
1306 self.account.set_password("a new password")
1307 self.assertRaises(AuthenticationError, refresh_discharge,
1308 self.caveat_id, self.discharge_macaroon.serialize())
1309
1310 def test_password_with_no_datetime(self):
1311 # simulate an "old" account password (before we started to keep the
1312 # changed date); note that I'm bypassing the AccountPassword save(),
1313 # which will update the attribute that I want in Null! (and we confirm
1314 # that in the assert)
1315 self.account.accountpassword.date_changed = None
1316 super(AccountPassword, self.account.accountpassword).save()
1317 assert self.account.accountpassword.date_changed is None
1318
1319 # check that auths ok
1320 refresh_discharge(self.caveat_id, self.discharge_macaroon.serialize())
1321
1322 def test_proper_refreshing(self):
1323 old_discharge = self.discharge_macaroon # just rename for readability
1324 service_location = settings.MACAROON_SERVICE_LOCATION
1325
1326 def get_value(search_key):
1327 for caveat in old_discharge.first_party_caveats():
1328 source, key, value = caveat.caveat_id.split("|", 2)
1329 if source == service_location and key == search_key:
1330 return value
1331
1332 # get old values from the macaroon and also change the account to see
1333 # that reflected
1334 old_last_auth = get_value('last_auth')
1335 old_expires = get_value('expires')
1336 new_mail = self.factory.make_email_for_account(
1337 self.account, status=EmailStatus.PREFERRED)
1338 self.account.displayname = "New test display name"
1339 self.account.save()
1340
1341 # call!
1342 before = now()
1343 new_discharge = refresh_discharge(self.caveat_id,
1344 old_discharge.serialize())
1345 after = now()
1346
1347 # test
1348 def checker(caveat):
1349 """Assure all caveats inside the discharged macaroon are ok."""
1350 source, key, value = caveat.split("|", 2)
1351
1352 if key == 'valid_since':
1353 valid_since = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
1354 self.assertGreater(valid_since, before)
1355 self.assertGreater(after, valid_since)
1356 return True
1357
1358 if key == 'last_auth':
1359 self.assertEqual(value, old_last_auth)
1360 return True
1361
1362 if key == 'account':
1363 acc = json.loads(base64.b64decode(value).decode("utf8"))
1364 self.assertEqual(acc['openid'], self.account.openid_identifier)
1365 self.assertEqual(acc['email'], new_mail.email)
1366 self.assertEqual(acc['displayname'], "New test display name")
1367 self.assertEqual(acc['is_verified'], self.account.is_verified)
1368 return True
1369
1370 if key == 'expires':
1371 self.assertEqual(value, old_expires)
1372 return True
1373
1374 # we're not validating an SSO from the discharged macaroon, fail!
1375 return False
1376
1377 # verify using the NEW discharge macaroon
1378 v = Verifier()
1379 v.satisfy_general(checker)
1380 v.verify(new_discharge, self.random_key, [])
12001381
=== modified file 'src/identityprovider/tests/test_forms.py'
--- src/identityprovider/tests/test_forms.py 2016-04-11 11:57:38 +0000
+++ src/identityprovider/tests/test_forms.py 2016-05-05 12:45:29 +0000
@@ -1138,7 +1138,7 @@
1138 """The server always returns discharge macaroons to trusted sites,1138 """The server always returns discharge macaroons to trusted sites,
1139 regardless of the state of the checkbox in the UI.1139 regardless of the state of the checkbox in the UI.
1140 """1140 """
1141 root_macaroon, _ = self.build_macaroon()1141 root_macaroon, _, _ = self.build_macaroon()
1142 macaroon_request = MacaroonRequest(root_macaroon.serialize())1142 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1143 form = MacaroonRequestForm(1143 form = MacaroonRequestForm(
1144 request=self._get_request_with_post_args(),1144 request=self._get_request_with_post_args(),
@@ -1149,7 +1149,7 @@
1149 """The server returns discharge macaroons to untrusted sites when1149 """The server returns discharge macaroons to untrusted sites when
1150 the user checks the checkbox in the UI.1150 the user checks the checkbox in the UI.
1151 """1151 """
1152 root_macaroon, _ = self.build_macaroon()1152 root_macaroon, _, _ = self.build_macaroon()
1153 macaroon_request = MacaroonRequest(root_macaroon.serialize())1153 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1154 form = MacaroonRequestForm(1154 form = MacaroonRequestForm(
1155 request=self._get_request_with_post_args(macaroon='macaroon'),1155 request=self._get_request_with_post_args(macaroon='macaroon'),
@@ -1160,7 +1160,7 @@
1160 """The server does not return discharge macaroons to untrusted sites1160 """The server does not return discharge macaroons to untrusted sites
1161 when the user does not check the checkbox in the UI.1161 when the user does not check the checkbox in the UI.
1162 """1162 """
1163 root_macaroon, _ = self.build_macaroon()1163 root_macaroon, _, _ = self.build_macaroon()
1164 macaroon_request = MacaroonRequest(root_macaroon.serialize())1164 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1165 form = MacaroonRequestForm(1165 form = MacaroonRequestForm(
1166 request=self._get_request_with_post_args(),1166 request=self._get_request_with_post_args(),
@@ -1169,7 +1169,7 @@
11691169
1170 def test_checkbox_status_for_trusted_site(self):1170 def test_checkbox_status_for_trusted_site(self):
1171 """Checkboxes are always checked if the site is trusted."""1171 """Checkboxes are always checked if the site is trusted."""
1172 root_macaroon, _ = self.build_macaroon()1172 root_macaroon, _, _ = self.build_macaroon()
1173 macaroon_request = MacaroonRequest(root_macaroon.serialize())1173 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1174 form = MacaroonRequestForm(1174 form = MacaroonRequestForm(
1175 request=self._get_request_with_post_args(),1175 request=self._get_request_with_post_args(),
@@ -1178,7 +1178,7 @@
11781178
1179 def test_checkbox_status_for_untrusted_site(self):1179 def test_checkbox_status_for_untrusted_site(self):
1180 """Checkboxes are checked by default if the site is untrusted."""1180 """Checkboxes are checked by default if the site is untrusted."""
1181 root_macaroon, _ = self.build_macaroon()1181 root_macaroon, _, _ = self.build_macaroon()
1182 macaroon_request = MacaroonRequest(root_macaroon.serialize())1182 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1183 form = MacaroonRequestForm(1183 form = MacaroonRequestForm(
1184 request=self._get_request_with_post_args(),1184 request=self._get_request_with_post_args(),
@@ -1189,7 +1189,7 @@
1189 """Checkboxes respect user preferences on untrusted sites where1189 """Checkboxes respect user preferences on untrusted sites where
1190 available.1190 available.
1191 """1191 """
1192 root_macaroon, _ = self.build_macaroon()1192 root_macaroon, _, _ = self.build_macaroon()
1193 macaroon_request = MacaroonRequest(root_macaroon.serialize())1193 macaroon_request = MacaroonRequest(root_macaroon.serialize())
1194 approved_data = {1194 approved_data = {
1195 'requested': ['macaroon'],1195 'requested': ['macaroon'],
11961196
=== modified file 'src/identityprovider/tests/test_macaroon.py'
--- src/identityprovider/tests/test_macaroon.py 2016-04-05 22:12:47 +0000
+++ src/identityprovider/tests/test_macaroon.py 2016-05-05 12:45:29 +0000
@@ -45,7 +45,7 @@
45 def setUp(self):45 def setUp(self):
46 super(MacaroonRequestTestCase, self).setUp()46 super(MacaroonRequestTestCase, self).setUp()
4747
48 self.root_macaroon, self.macaroon_random_key = self.build_macaroon()48 self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon()
49 self.req = MacaroonRequest(self.root_macaroon.serialize())49 self.req = MacaroonRequest(self.root_macaroon.serialize())
5050
51 def assertMacaroonsEqual(self, expected, observed):51 def assertMacaroonsEqual(self, expected, observed):
5252
=== modified file 'src/identityprovider/tests/test_views_server.py'
--- src/identityprovider/tests/test_views_server.py 2016-04-04 15:41:06 +0000
+++ src/identityprovider/tests/test_views_server.py 2016-05-05 12:45:29 +0000
@@ -227,7 +227,7 @@
227 self._test_auto_auth(sreg=['fullname'])227 self._test_auto_auth(sreg=['fullname'])
228228
229 def test_handle_user_response_auto_auth_discharge_macaroon(self):229 def test_handle_user_response_auto_auth_discharge_macaroon(self):
230 root_macaroon, macaroon_random_key = self.build_macaroon()230 root_macaroon, macaroon_random_key, _ = self.build_macaroon()
231 # Add padding to force a POST after signing. We don't know exactly231 # Add padding to force a POST after signing. We don't know exactly
232 # how long the serialized discharge macaroon will be yet, but it232 # how long the serialized discharge macaroon will be yet, but it
233 # will probably be at least 1024 bytes.233 # will probably be at least 1024 bytes.
@@ -1065,7 +1065,7 @@
1065 # make sure rpconfig is set to auto authorize1065 # make sure rpconfig is set to auto authorize
1066 OpenIDRPConfig.objects.create(1066 OpenIDRPConfig.objects.create(
1067 trust_root='http://localhost/', auto_authorize=True)1067 trust_root='http://localhost/', auto_authorize=True)
1068 root_macaroon, macaroon_random_key = self.build_macaroon()1068 root_macaroon, macaroon_random_key, _ = self.build_macaroon()
1069 param_overrides = {1069 param_overrides = {
1070 'openid.ns.macaroon': MACAROON_NS,1070 'openid.ns.macaroon': MACAROON_NS,
1071 'openid.macaroon.root': root_macaroon.serialize(),1071 'openid.macaroon.root': root_macaroon.serialize(),
@@ -1089,7 +1089,7 @@
1089 root_macaroon, macaroon_random_key, [discharge_macaroon]))1089 root_macaroon, macaroon_random_key, [discharge_macaroon]))
10901090
1091 def test_state_of_checkboxes_and_data_formats_macaroon(self):1091 def test_state_of_checkboxes_and_data_formats_macaroon(self):
1092 root_macaroon, _ = self.build_macaroon()1092 root_macaroon, _, _ = self.build_macaroon()
1093 param_overrides = {1093 param_overrides = {
1094 'openid.ns.macaroon': MACAROON_NS,1094 'openid.ns.macaroon': MACAROON_NS,
1095 'openid.macaroon.root': root_macaroon.serialize(),1095 'openid.macaroon.root': root_macaroon.serialize(),
@@ -1864,7 +1864,7 @@
1864 if with_teams:1864 if with_teams:
1865 params['openid.lp.query_membership'] = 'ubuntu-team'1865 params['openid.lp.query_membership'] = 'ubuntu-team'
1866 if with_macaroon:1866 if with_macaroon:
1867 root_macaroon, _ = self.build_macaroon()1867 root_macaroon, _, _ = self.build_macaroon()
1868 params['openid.ns.macaroon'] = MACAROON_NS1868 params['openid.ns.macaroon'] = MACAROON_NS
1869 params['openid.macaroon.root'] = root_macaroon.serialize()1869 params['openid.macaroon.root'] = root_macaroon.serialize()
1870 provider_url = get_provider_url(request)1870 provider_url = get_provider_url(request)
18711871
=== modified file 'src/identityprovider/tests/utils.py'
--- src/identityprovider/tests/utils.py 2016-04-28 13:01:53 +0000
+++ src/identityprovider/tests/utils.py 2016-05-05 12:45:29 +0000
@@ -202,7 +202,7 @@
202 test_rsa_pub_key.encrypt(json.dumps(info), 32)[0])202 test_rsa_pub_key.encrypt(json.dumps(info), 32)[0])
203 root_macaroon.add_third_party_caveat(203 root_macaroon.add_third_party_caveat(
204 service_location, random_key, info_encrypted)204 service_location, random_key, info_encrypted)
205 return root_macaroon, macaroon_random_key205 return root_macaroon, macaroon_random_key, random_key
206206
207207
208class SSOBaseTestCase(SSOBaseTestCaseMixin, TestCase):208class SSOBaseTestCase(SSOBaseTestCaseMixin, TestCase):
209209
=== modified file 'src/identityprovider/views/server.py'
--- src/identityprovider/views/server.py 2016-04-29 14:17:15 +0000
+++ src/identityprovider/views/server.py 2016-05-05 12:45:29 +0000
@@ -42,7 +42,7 @@
42from openid.yadis.constants import YADIS_HEADER_NAME42from openid.yadis.constants import YADIS_HEADER_NAME
4343
44from identityprovider import signed44from identityprovider import signed
45from identityprovider.auth import build_discharge_macaroon45from identityprovider.auth import build_discharge_macaroon_from_root
46from identityprovider.const import (46from identityprovider.const import (
47 AX_DATA_FIELDS,47 AX_DATA_FIELDS,
48 MACAROON_NS,48 MACAROON_NS,
@@ -769,7 +769,7 @@
769 rpconfig = utils.get_rpconfig(openid_request.trust_root)769 rpconfig = utils.get_rpconfig(openid_request.trust_root)
770 form = MacaroonRequestForm(request, macaroon_request, rpconfig)770 form = MacaroonRequestForm(request, macaroon_request, rpconfig)
771 if form.data_approved_for_request:771 if form.data_approved_for_request:
772 discharge_macaroon = build_discharge_macaroon(772 discharge_macaroon = build_discharge_macaroon_from_root(
773 request.user, macaroon_request.root_macaroon_raw)773 request.user, macaroon_request.root_macaroon_raw)
774 macaroon_response = MacaroonResponse.extractResponse(774 macaroon_response = MacaroonResponse.extractResponse(
775 macaroon_request, discharge_macaroon.serialize())775 macaroon_request, discharge_macaroon.serialize())