Merge lp:~facundo/canonical-identity-provider/support-caveat-v1 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: 1447
Proposed branch: lp:~facundo/canonical-identity-provider/support-caveat-v1
Merge into: lp:canonical-identity-provider/release
Diff against target: 224 lines (+89/-39)
3 files modified
src/api/v20/tests/test_handlers.py (+11/-16)
src/identityprovider/auth.py (+30/-5)
src/identityprovider/tests/test_auth.py (+48/-18)
To merge this branch: bzr merge lp:~facundo/canonical-identity-provider/support-caveat-v1
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+293941@code.launchpad.net

Commit message

Support v0 (unversioned) and v1 caveat_ids.

Description of the change

Support v0 (unversioned) and v1 caveat_ids.

To post a comment you must log in.
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/tests/test_handlers.py'
--- src/api/v20/tests/test_handlers.py 2016-05-05 15:39:31 +0000
+++ src/api/v20/tests/test_handlers.py 2016-05-05 20:58:38 +0000
@@ -2176,17 +2176,7 @@
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')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))
21902180
21912181
2192class MacaroonDischargeHandlerTestCase(MacaroonHandlerBaseTestCase,2182class MacaroonDischargeHandlerTestCase(MacaroonHandlerBaseTestCase,
@@ -2342,7 +2332,8 @@
2342 def test_macaroon_created(self):2332 def test_macaroon_created(self):
2343 json_body = self.do_post()2333 json_body = self.do_post()
2344 self.assert_response_correct(json_body, self.account)2334 self.assert_response_correct(json_body, self.account)
2345 self.check_stats_increment('macaroon.discharge', key='single_macaroon')2335 self.stats_increment.assert_any_call(
2336 'macaroon.discharge', key='single_macaroon')
23462337
2347 def test_root_macaroon_corrupt(self):2338 def test_root_macaroon_corrupt(self):
2348 data = dict(2339 data = dict(
@@ -2388,7 +2379,8 @@
2388 v.satisfy_general(verifying_function)2379 v.satisfy_general(verifying_function)
2389 v.verify(root_macaroon, key, [discharge_macaroon])2380 v.verify(root_macaroon, key, [discharge_macaroon])
23902381
2391 self.check_stats_increment('macaroon.discharge', key='multi_macaroons')2382 self.stats_increment.assert_any_call(
2383 'macaroon.discharge', key='multi_macaroons')
23922384
2393 def test_multiple_mixed(self):2385 def test_multiple_mixed(self):
2394 bad_macaroon, _, _ = self.build_macaroon(2386 bad_macaroon, _, _ = self.build_macaroon(
@@ -2410,7 +2402,8 @@
24102402
2411 json_body = self.do_post(data=data)2403 json_body = self.do_post(data=data)
2412 self.assert_response_correct(json_body, self.account, bind=True)2404 self.assert_response_correct(json_body, self.account, bind=True)
2413 self.check_stats_increment('macaroon.discharge', key='caveat_id')2405 self.stats_increment.assert_any_call(
2406 'macaroon.discharge', key='caveat_id')
24142407
2415 def test_caveat_id_corrupt(self):2408 def test_caveat_id_corrupt(self):
2416 data = dict(email='foo@bar.com', password='foobar123',2409 data = dict(email='foo@bar.com', password='foobar123',
@@ -2545,7 +2538,8 @@
2545 v.satisfy_general(lambda c: True)2538 v.satisfy_general(lambda c: True)
2546 v.verify(2539 v.verify(
2547 self.root_macaroon, self.macaroon_random_key, [discharge_macaroon])2540 self.root_macaroon, self.macaroon_random_key, [discharge_macaroon])
2548 self.check_stats_increment('macaroon.refresh', key='root_macaroon')2541 self.stats_increment.assert_any_call(
2542 'macaroon.refresh', key='root_macaroon')
25492543
2550 def test_just_discharge_refreshed(self):2544 def test_just_discharge_refreshed(self):
2551 json_body = self.do_post()2545 json_body = self.do_post()
@@ -2559,4 +2553,5 @@
2559 v = Verifier()2553 v = Verifier()
2560 v.satisfy_general(lambda c: True)2554 v.satisfy_general(lambda c: True)
2561 v.verify(self.root_macaroon, self.macaroon_random_key, [discharge])2555 v.verify(self.root_macaroon, self.macaroon_random_key, [discharge])
2562 self.check_stats_increment('macaroon.refresh', key='just_discharge')2556 self.stats_increment.assert_any_call(
2557 'macaroon.refresh', key='just_discharge')
25632558
=== modified file 'src/identityprovider/auth.py'
--- src/identityprovider/auth.py 2016-05-05 15:39:31 +0000
+++ src/identityprovider/auth.py 2016-05-05 20:58:38 +0000
@@ -35,6 +35,7 @@
35)35)
36from identityprovider.models.api import APIUser36from identityprovider.models.api import APIUser
37from identityprovider.models.const import EmailStatus, TokenScope37from identityprovider.models.const import EmailStatus, TokenScope
38from identityprovider.stats import stats
38from identityprovider.timeline_helpers import (39from identityprovider.timeline_helpers import (
39 get_request_timing_function,40 get_request_timing_function,
40 maybe_contextmanager,41 maybe_contextmanager,
@@ -511,16 +512,40 @@
511 return sso_caveat.caveat_id, caveat_info512 return sso_caveat.caveat_id, caveat_info
512513
513514
515def _open_caveat_v0(info):
516 """Open the caveat_id, v0 (unversioned, really) format."""
517 caveat_info_raw = settings.CRYPTO_SSO_PRIVKEY.decrypt(
518 base64.b64decode(info))
519 caveat_info = json.loads(caveat_info_raw)
520 return caveat_info
521
522
523def _open_caveat_v1(info):
524 """Open the caveat_id, v1 format."""
525 assert info['version'] == 1 # only supported version
526 info['3rdparty'] = settings.CRYPTO_SSO_PRIVKEY.decrypt(
527 base64.b64decode(info.pop('secret')))
528 return info
529
530
514def _decrypt_caveat(raw_caveat_id):531def _decrypt_caveat(raw_caveat_id):
515 """Decrypt and decode the caveat id info."""532 """Decrypt and decode the caveat id info."""
516 try:533 try:
517 caveat_info_raw = settings.CRYPTO_SSO_PRIVKEY.decrypt(534 caveat_info = json.loads(raw_caveat_id)
518 base64.b64decode(raw_caveat_id))535 function = _open_caveat_v1
519 caveat_info = json.loads(caveat_info_raw)536 stats_key = '1'
520 except:537 except:
521 # not properly encrypted information inside538 # bad data, or maybe the v0 (unversioned, really) format
539 caveat_info = raw_caveat_id
540 function = _open_caveat_v0
541 stats_key = '0'
542
543 try:
544 caveat_info = function(caveat_info)
545 except:
522 raise ValidationError("Bad info in the caveat_id")546 raise ValidationError("Bad info in the caveat_id")
523547
548 stats.increment('macaroon.auth.caveat_version', key=stats_key)
524 return caveat_info549 return caveat_info
525550
526551
527552
=== modified file 'src/identityprovider/tests/test_auth.py'
--- src/identityprovider/tests/test_auth.py 2016-05-05 15:39:31 +0000
+++ src/identityprovider/tests/test_auth.py 2016-05-05 20:58:38 +0000
@@ -23,7 +23,7 @@
23from django.test.utils import override_settings23from django.test.utils import override_settings
24from django.utils.timezone import now24from django.utils.timezone import now
25from mock import call, Mock, patch25from mock import call, Mock, patch
26from pymacaroons import Macaroon, Verifier26from pymacaroons import Verifier
2727
28from identityprovider.auth import (28from identityprovider.auth import (
29 LaunchpadBackend,29 LaunchpadBackend,
@@ -1108,36 +1108,66 @@
11081108
1109class MacaroonHelpersTestCase(SSOBaseTestCase):1109class MacaroonHelpersTestCase(SSOBaseTestCase):
11101110
1111 def test_get_caveat_ok(self):1111 def setUp(self):
1112 test_rsa_priv_key, test_rsa_pub_key = self.setup_key_pair()1112 super(MacaroonHelpersTestCase, self).setUp()
1113 self.stats_increment = self.patch(
1114 'identityprovider.auth.stats.increment')
11131115
1114 # create a Macaron with the proper third party caveat1116 def test_get_caveat_ok_v0(self):
1115 macaroon_random_key = binascii.hexlify(os.urandom(32))
1116 root_macaroon = Macaroon(
1117 location='The store ;)',
1118 key=macaroon_random_key,
1119 identifier='A test macaroon',
1120 )
1121 random_key = binascii.hexlify(os.urandom(32))
1122 info = {1117 info = {
1123 'some stuff': 'foo',1118 'some stuff': 'foo',
1124 'more stuff': 'bar',1119 'more stuff': 'bar',
1125 }1120 }
1121 _, test_rsa_pub_key = self.setup_key_pair()
1126 info_encrypted = base64.b64encode(1122 info_encrypted = base64.b64encode(
1127 test_rsa_pub_key.encrypt(json.dumps(info), 32)[0])1123 test_rsa_pub_key.encrypt(json.dumps(info), 32)[0])
1128 root_macaroon.add_third_party_caveat(
1129 settings.MACAROON_SERVICE_LOCATION, random_key, info_encrypted)
11301124
1131 # check1125 # check
1132 (sso_caveat,) = [c for c in root_macaroon.third_party_caveats()1126 caveat_info = _decrypt_caveat(info_encrypted)
1133 if c.location == settings.MACAROON_SERVICE_LOCATION]
1134 caveat_info = _decrypt_caveat(sso_caveat.caveat_id)
1135 self.assertEqual(caveat_info, info)1127 self.assertEqual(caveat_info, info)
11361128 self.stats_increment.assert_any_call(
1137 def test_get_caveat_badly_encrypted(self):1129 'macaroon.auth.caveat_version', key='0')
1130
1131 def test_get_caveat_ok_v1(self):
1132 random_key = binascii.hexlify(os.urandom(32))
1133 _, test_rsa_pub_key = self.setup_key_pair()
1134 caveat_id = json.dumps({
1135 'version': 1,
1136 'secret': base64.b64encode(
1137 test_rsa_pub_key.encrypt(random_key, 32)[0]),
1138 })
1139
1140 # check
1141 caveat_info = _decrypt_caveat(caveat_id)
1142 self.assertEqual(caveat_info, {
1143 'version': 1,
1144 '3rdparty': random_key,
1145 })
1146 self.stats_increment.assert_any_call(
1147 'macaroon.auth.caveat_version', key='1')
1148
1149 def test_get_caveat_v0_badly_encrypted(self):
1138 self.assertRaises(ValidationError,1150 self.assertRaises(ValidationError,
1139 _decrypt_caveat, b"not really well encrypted stuff")1151 _decrypt_caveat, b"not really well encrypted stuff")
11401152
1153 def test_get_caveat_v1_bad_version(self):
1154 random_key = binascii.hexlify(os.urandom(32))
1155 _, test_rsa_pub_key = self.setup_key_pair()
1156 caveat_id = json.dumps({
1157 'version': 7,
1158 'secret': base64.b64encode(
1159 test_rsa_pub_key.encrypt(random_key, 32)[0]),
1160 })
1161 self.assertRaises(ValidationError, _decrypt_caveat, caveat_id)
1162
1163 def test_get_caveat_v1_badly_encrypted(self):
1164 _, test_rsa_pub_key = self.setup_key_pair()
1165 caveat_id = json.dumps({
1166 'version': 1,
1167 'secret': "badly encrypted",
1168 })
1169 self.assertRaises(ValidationError, _decrypt_caveat, caveat_id)
1170
11411171
1142class MacaroonRefreshFromRootTestCase(SSOBaseTestCase):1172class MacaroonRefreshFromRootTestCase(SSOBaseTestCase):
1143 """Test the deprecated refresh_macaroons.1173 """Test the deprecated refresh_macaroons.