Merge lp:~cjwatson/canonical-identity-provider/openid-macaroon-discharge-v1 into lp:canonical-identity-provider/release
- openid-macaroon-discharge-v1
- Merge into trunk
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1452 |
Proposed branch: | lp:~cjwatson/canonical-identity-provider/openid-macaroon-discharge-v1 |
Merge into: | lp:canonical-identity-provider/release |
Diff against target: |
672 lines (+141/-124) 10 files modified
src/api/v20/tests/test_handlers.py (+12/-5) src/identityprovider/forms.py (+11/-6) src/identityprovider/macaroon.py (+12/-26) src/identityprovider/tests/test_auth.py (+14/-11) src/identityprovider/tests/test_forms.py (+19/-20) src/identityprovider/tests/test_macaroon.py (+12/-16) src/identityprovider/tests/test_views_server.py (+17/-9) src/identityprovider/tests/utils.py (+30/-8) src/identityprovider/views/server.py (+9/-7) src/webui/views/consumer.py (+5/-16) |
To merge this branch: | bzr merge lp:~cjwatson/canonical-identity-provider/openid-macaroon-discharge-v1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ricardo Kirkner (community) | Approve | ||
Review via email: mp+294272@code.launchpad.net |
Commit message
Switch the OpenID discharge macaroon protocol over to the new caveat-only mechanism and v1 caveat IDs.
Description of the change
Switch the OpenID discharge macaroon protocol over to the new caveat-only mechanism and v1 caveat IDs.
v0 caveat IDs should in fact still be accepted for the time being, but we won't support further features built on top of this branch such as human-readable descriptions with them, and we only test the new format.
I didn't bother keeping the older OpenID macaroon protocol around, because AFAIK it has no users yet other than my WIP changes to Launchpad.
The Launchpad side of this is in https:/
To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) : | # |
Revision history for this message
Colin Watson (cjwatson) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/api/v20/tests/test_handlers.py' | |||
2 | --- src/api/v20/tests/test_handlers.py 2016-05-05 20:58:23 +0000 | |||
3 | +++ src/api/v20/tests/test_handlers.py 2016-05-11 17:42:55 +0000 | |||
4 | @@ -2169,7 +2169,11 @@ | |||
5 | 2169 | self.login_failed_calls = [] | 2169 | self.login_failed_calls = [] |
6 | 2170 | login_failed.connect(self.track_failed_logins, dispatch_uid=self.id()) | 2170 | login_failed.connect(self.track_failed_logins, dispatch_uid=self.id()) |
7 | 2171 | 2171 | ||
9 | 2172 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon() | 2172 | # XXX cjwatson 2016-05-11: |
10 | 2173 | # MacaroonRefreshHandlerTestCase.test_macaroon_refreshed needs to be | ||
11 | 2174 | # fixed before changing the version here. | ||
12 | 2175 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon( | ||
13 | 2176 | version=0) | ||
14 | 2173 | self.data = dict( | 2177 | self.data = dict( |
15 | 2174 | email='foo@bar.com', password='foobar123', | 2178 | email='foo@bar.com', password='foobar123', |
16 | 2175 | macaroon=self.root_macaroon.serialize()) | 2179 | macaroon=self.root_macaroon.serialize()) |
17 | @@ -2344,7 +2348,8 @@ | |||
18 | 2344 | check_login_failed=False) | 2348 | check_login_failed=False) |
19 | 2345 | 2349 | ||
20 | 2346 | def test_root_macaroon_not_for_sso(self): | 2350 | def test_root_macaroon_not_for_sso(self): |
22 | 2347 | macaroon, _, _ = self.build_macaroon(service_location="other service") | 2351 | macaroon, _, _ = self.build_macaroon( |
23 | 2352 | service_location="other service", version=1) | ||
24 | 2348 | data = dict(email='foo@bar.com', password='foobar123', | 2353 | data = dict(email='foo@bar.com', password='foobar123', |
25 | 2349 | macaroon=macaroon.serialize()) | 2354 | macaroon=macaroon.serialize()) |
26 | 2350 | self.assert_failed_login('INVALID_CREDENTIALS', data, | 2355 | self.assert_failed_login('INVALID_CREDENTIALS', data, |
27 | @@ -2360,7 +2365,8 @@ | |||
28 | 2360 | 2365 | ||
29 | 2361 | def test_multiple_macaroons(self): | 2366 | def test_multiple_macaroons(self): |
30 | 2362 | # build *several* macaroons and send them all at once | 2367 | # build *several* macaroons and send them all at once |
32 | 2363 | roots, rkeys, _ = zip(*[self.build_macaroon() for _ in range(3)]) | 2368 | roots, rkeys, _ = zip(*[ |
33 | 2369 | self.build_macaroon(version=1) for _ in range(3)]) | ||
34 | 2364 | ids = range(3) | 2370 | ids = range(3) |
35 | 2365 | roots = dict(zip(ids, roots)) | 2371 | roots = dict(zip(ids, roots)) |
36 | 2366 | rkeys = dict(zip(ids, rkeys)) | 2372 | rkeys = dict(zip(ids, rkeys)) |
37 | @@ -2384,7 +2390,7 @@ | |||
38 | 2384 | 2390 | ||
39 | 2385 | def test_multiple_mixed(self): | 2391 | def test_multiple_mixed(self): |
40 | 2386 | bad_macaroon, _, _ = self.build_macaroon( | 2392 | bad_macaroon, _, _ = self.build_macaroon( |
42 | 2387 | service_location="other service") | 2393 | service_location="other service", version=1) |
43 | 2388 | data = dict(email='foo@bar.com', password='foobar123', macaroons=[ | 2394 | data = dict(email='foo@bar.com', password='foobar123', macaroons=[ |
44 | 2389 | ('good', self.root_macaroon.serialize()), | 2395 | ('good', self.root_macaroon.serialize()), |
45 | 2390 | ('bad', bad_macaroon.serialize()), | 2396 | ('bad', bad_macaroon.serialize()), |
46 | @@ -2518,7 +2524,8 @@ | |||
47 | 2518 | check_login_failed=False) | 2524 | check_login_failed=False) |
48 | 2519 | 2525 | ||
49 | 2520 | def test_macaroon_bad_authinfo(self): | 2526 | def test_macaroon_bad_authinfo(self): |
51 | 2521 | macaroon, _, _ = self.build_macaroon(service_location="other service") | 2527 | macaroon, _, _ = self.build_macaroon( |
52 | 2528 | service_location="other service", version=1) | ||
53 | 2522 | data = dict(root_macaroon=macaroon.serialize(), | 2529 | data = dict(root_macaroon=macaroon.serialize(), |
54 | 2523 | discharge_macaroon=self.discharge_macaroon.serialize()) | 2530 | discharge_macaroon=self.discharge_macaroon.serialize()) |
55 | 2524 | self.assert_failed_login('INVALID_CREDENTIALS', data, | 2531 | self.assert_failed_login('INVALID_CREDENTIALS', data, |
56 | 2525 | 2532 | ||
57 | === modified file 'src/identityprovider/forms.py' | |||
58 | --- src/identityprovider/forms.py 2016-05-03 12:21:13 +0000 | |||
59 | +++ src/identityprovider/forms.py 2016-05-11 17:42:55 +0000 | |||
60 | @@ -574,12 +574,13 @@ | |||
61 | 574 | class MacaroonRequestForm(Form): | 574 | class MacaroonRequestForm(Form): |
62 | 575 | """A form object for user control over requesting discharge macaroons.""" | 575 | """A form object for user control over requesting discharge macaroons.""" |
63 | 576 | 576 | ||
65 | 577 | def __init__(self, request, macaroon_request, rpconfig, | 577 | def __init__(self, request, macaroon_request, rpconfig, trust_root, |
66 | 578 | approved_data=None): | 578 | approved_data=None): |
67 | 579 | self.request = request | 579 | self.request = request |
68 | 580 | self.macaroon_request = macaroon_request | 580 | self.macaroon_request = macaroon_request |
70 | 581 | self.root_macaroon = self.macaroon_request.root_macaroon | 581 | self.caveat_id = self.macaroon_request.caveat_id |
71 | 582 | self.rpconfig = rpconfig | 582 | self.rpconfig = rpconfig |
72 | 583 | self.trust_root = trust_root | ||
73 | 583 | self.approved_data = approved_data | 584 | self.approved_data = approved_data |
74 | 584 | self.data = self._get_data_for_user() | 585 | self.data = self._get_data_for_user() |
75 | 585 | super(MacaroonRequestForm, self).__init__(self.data) | 586 | super(MacaroonRequestForm, self).__init__(self.data) |
76 | @@ -590,14 +591,18 @@ | |||
77 | 590 | def _get_data_for_user(self): | 591 | def _get_data_for_user(self): |
78 | 591 | """Data to ask about in the form.""" | 592 | """Data to ask about in the form.""" |
79 | 592 | data = {} | 593 | data = {} |
82 | 593 | if self.root_macaroon: | 594 | if self.caveat_id: |
83 | 594 | data['macaroon'] = self.root_macaroon | 595 | data['macaroon'] = self.caveat_id |
84 | 595 | return data | 596 | return data |
85 | 596 | 597 | ||
86 | 597 | def _init_fields(self, data): | 598 | def _init_fields(self, data): |
87 | 598 | """Initialise fields from the discharge macaroon request.""" | 599 | """Initialise fields from the discharge macaroon request.""" |
90 | 599 | for location, root_macaroon in data.iteritems(): | 600 | for location, caveat_id in data.iteritems(): |
91 | 600 | label = 'Service authorization for %s' % root_macaroon.location | 601 | if self.rpconfig: |
92 | 602 | rp_displayname = self.rpconfig.displayname | ||
93 | 603 | else: | ||
94 | 604 | rp_displayname = self.trust_root | ||
95 | 605 | label = 'Service authorization for %s' % rp_displayname | ||
96 | 601 | self.fields[location] = fields.BooleanField( | 606 | self.fields[location] = fields.BooleanField( |
97 | 602 | label=label, widget=forms.CheckboxInput( | 607 | label=label, widget=forms.CheckboxInput( |
98 | 603 | check_test=self.check_test(location))) | 608 | check_test=self.check_test(location))) |
99 | 604 | 609 | ||
100 | === modified file 'src/identityprovider/macaroon.py' | |||
101 | --- src/identityprovider/macaroon.py 2016-04-05 22:12:47 +0000 | |||
102 | +++ src/identityprovider/macaroon.py 2016-05-11 17:42:55 +0000 | |||
103 | @@ -19,8 +19,9 @@ | |||
104 | 19 | 19 | ||
105 | 20 | It must be set to: http://ns.login.ubuntu.com/2016/openid-macaroon | 20 | It must be set to: http://ns.login.ubuntu.com/2016/openid-macaroon |
106 | 21 | 21 | ||
109 | 22 | openid.macaroon.root | 22 | openid.macaroon.caveat_id |
110 | 23 | The serialised root macaroon that the RP wants to discharge. | 23 | The SSO third-party caveat ID from the root macaroon that the RP wants |
111 | 24 | to discharge. | ||
112 | 24 | 25 | ||
113 | 25 | As part of the positive assertion OpenID response, the following fields | 26 | As part of the positive assertion OpenID response, the following fields |
114 | 26 | will be provided: | 27 | will be provided: |
115 | @@ -38,7 +39,6 @@ | |||
116 | 38 | NamespaceAliasRegistrationError, | 39 | NamespaceAliasRegistrationError, |
117 | 39 | registerNamespaceAlias, | 40 | registerNamespaceAlias, |
118 | 40 | ) | 41 | ) |
119 | 41 | from pymacaroons import Macaroon | ||
120 | 42 | 42 | ||
121 | 43 | from identityprovider.const import MACAROON_NS | 43 | from identityprovider.const import MACAROON_NS |
122 | 44 | 44 | ||
123 | @@ -101,26 +101,22 @@ | |||
124 | 101 | class MacaroonRequest(Extension): | 101 | class MacaroonRequest(Extension): |
125 | 102 | """An object to hold the state of a discharge macaroon request. | 102 | """An object to hold the state of a discharge macaroon request. |
126 | 103 | 103 | ||
129 | 104 | @ivar root_macaroon_raw: The serialised root macaroon to discharge. | 104 | @ivar caveat_id: The SSO third-party caveat ID from the root macaroon |
130 | 105 | @type root_macaroon_raw: str | 105 | that the RP wants to discharge. |
131 | 106 | @type caveat_id: str | ||
132 | 106 | 107 | ||
135 | 107 | @group Consumer: requestField, requestMacaroon, getExtensionArgs, | 108 | @group Consumer: requestField, getExtensionArgs, addToOpenIDRequest |
134 | 108 | addToOpenIDRequest | ||
136 | 109 | @group Server: fromOpenIDRequest, parseExtensionArgs | 109 | @group Server: fromOpenIDRequest, parseExtensionArgs |
137 | 110 | """ | 110 | """ |
138 | 111 | 111 | ||
139 | 112 | ns_alias = 'macaroon' | 112 | ns_alias = 'macaroon' |
140 | 113 | 113 | ||
142 | 114 | def __init__(self, root_macaroon_raw=None, macaroon_ns_uri=MACAROON_NS): | 114 | def __init__(self, caveat_id=None, macaroon_ns_uri=MACAROON_NS): |
143 | 115 | """Initialize an empty discharge macaroon request.""" | 115 | """Initialize an empty discharge macaroon request.""" |
144 | 116 | Extension.__init__(self) | 116 | Extension.__init__(self) |
147 | 117 | self.root_macaroon_raw = root_macaroon_raw | 117 | self.caveat_id = caveat_id |
146 | 118 | self.root_macaroon = None | ||
148 | 119 | self.ns_uri = macaroon_ns_uri | 118 | self.ns_uri = macaroon_ns_uri |
149 | 120 | 119 | ||
150 | 121 | if root_macaroon_raw: | ||
151 | 122 | self.requestMacaroon(root_macaroon_raw) | ||
152 | 123 | |||
153 | 124 | @classmethod | 120 | @classmethod |
154 | 125 | def fromOpenIDRequest(cls, request): | 121 | def fromOpenIDRequest(cls, request): |
155 | 126 | """Create a discharge macaroon request that contains the fields that | 122 | """Create a discharge macaroon request that contains the fields that |
156 | @@ -166,17 +162,7 @@ | |||
157 | 166 | 162 | ||
158 | 167 | @returns: None; updates this object | 163 | @returns: None; updates this object |
159 | 168 | """ | 164 | """ |
171 | 169 | self.root_macaroon_raw = args.get('root') | 165 | self.caveat_id = args.get('caveat_id') |
161 | 170 | if self.root_macaroon_raw: | ||
162 | 171 | self.requestMacaroon(self.root_macaroon_raw) | ||
163 | 172 | |||
164 | 173 | def requestMacaroon(self, root_macaroon_raw): | ||
165 | 174 | """Request a discharge macaroon. | ||
166 | 175 | |||
167 | 176 | @param root_macaroon_raw: The serialised root macaroon to discharge. | ||
168 | 177 | @type root_macaroon_raw: str | ||
169 | 178 | """ | ||
170 | 179 | self.root_macaroon = Macaroon.deserialize(root_macaroon_raw) | ||
172 | 180 | 166 | ||
173 | 181 | def getExtensionArgs(self): | 167 | def getExtensionArgs(self): |
174 | 182 | """Get a dictionary of unqualified macaroon request parameters | 168 | """Get a dictionary of unqualified macaroon request parameters |
175 | @@ -189,8 +175,8 @@ | |||
176 | 189 | """ | 175 | """ |
177 | 190 | args = {} | 176 | args = {} |
178 | 191 | 177 | ||
181 | 192 | if self.root_macaroon_raw: | 178 | if self.caveat_id: |
182 | 193 | args['root'] = self.root_macaroon_raw | 179 | args['caveat_id'] = self.caveat_id |
183 | 194 | 180 | ||
184 | 195 | return args | 181 | return args |
185 | 196 | 182 | ||
186 | 197 | 183 | ||
187 | === modified file 'src/identityprovider/tests/test_auth.py' | |||
188 | --- src/identityprovider/tests/test_auth.py 2016-05-05 20:58:23 +0000 | |||
189 | +++ src/identityprovider/tests/test_auth.py 2016-05-11 17:42:55 +0000 | |||
190 | @@ -968,14 +968,16 @@ | |||
191 | 968 | 968 | ||
192 | 969 | def setUp(self): | 969 | def setUp(self): |
193 | 970 | super(BuildMacaroonFromRootDischargeTestCase, self).setUp() | 970 | super(BuildMacaroonFromRootDischargeTestCase, self).setUp() |
195 | 971 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon() | 971 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon( |
196 | 972 | version=0) | ||
197 | 972 | 973 | ||
198 | 973 | def test_root_macaroon_corrupt(self): | 974 | def test_root_macaroon_corrupt(self): |
199 | 974 | self.assertRaises(ValidationError, build_discharge_macaroon_from_root, | 975 | self.assertRaises(ValidationError, build_discharge_macaroon_from_root, |
200 | 975 | "fake account", "I'm a seriously corrupted macaroon") | 976 | "fake account", "I'm a seriously corrupted macaroon") |
201 | 976 | 977 | ||
202 | 977 | def test_root_macaroon_not_for_sso(self): | 978 | def test_root_macaroon_not_for_sso(self): |
204 | 978 | macaroon, _, _ = self.build_macaroon(service_location="other service") | 979 | macaroon, _, _ = self.build_macaroon( |
205 | 980 | service_location="other service", version=0) | ||
206 | 979 | self.assertRaises(AuthenticationError, | 981 | self.assertRaises(AuthenticationError, |
207 | 980 | build_discharge_macaroon_from_root, | 982 | build_discharge_macaroon_from_root, |
208 | 981 | "fake account", macaroon.serialize()) | 983 | "fake account", macaroon.serialize()) |
209 | @@ -1043,18 +1045,18 @@ | |||
210 | 1043 | "fake account", "I'm a seriously corrupted caveatid") | 1045 | "fake account", "I'm a seriously corrupted caveatid") |
211 | 1044 | 1046 | ||
212 | 1045 | def test_caveat_id_not_for_sso(self): | 1047 | def test_caveat_id_not_for_sso(self): |
216 | 1046 | macaroon, _, _ = self.build_macaroon(service_location="other service") | 1048 | macaroon, _, _ = self.build_macaroon( |
217 | 1047 | (sso_caveat,) = [c for c in macaroon.third_party_caveats() | 1049 | service_location="other service", version=1) |
218 | 1048 | if c.location == "other service"] | 1050 | sso_caveat = self.get_sso_caveat( |
219 | 1051 | macaroon, service_location="other service") | ||
220 | 1049 | 1052 | ||
221 | 1050 | self.assertRaises(ValidationError, build_discharge_macaroon, | 1053 | self.assertRaises(ValidationError, build_discharge_macaroon, |
222 | 1051 | "fake account", sso_caveat) | 1054 | "fake account", sso_caveat) |
223 | 1052 | 1055 | ||
224 | 1053 | def test_proper_discharging(self): | 1056 | def test_proper_discharging(self): |
225 | 1054 | # build the input and call | 1057 | # build the input and call |
229 | 1055 | root_macaroon, _, random_key = self.build_macaroon() | 1058 | root_macaroon, _, random_key = self.build_macaroon(version=1) |
230 | 1056 | (sso_caveat,) = [c for c in root_macaroon.third_party_caveats() | 1059 | sso_caveat = self.get_sso_caveat(root_macaroon) |
228 | 1057 | if c.location == settings.MACAROON_SERVICE_LOCATION] | ||
231 | 1058 | 1060 | ||
232 | 1059 | real_account = self.factory.make_account() | 1061 | real_account = self.factory.make_account() |
233 | 1060 | before = now() | 1062 | before = now() |
234 | @@ -1177,7 +1179,8 @@ | |||
235 | 1177 | 1179 | ||
236 | 1178 | def setUp(self): | 1180 | def setUp(self): |
237 | 1179 | super(MacaroonRefreshFromRootTestCase, self).setUp() | 1181 | super(MacaroonRefreshFromRootTestCase, self).setUp() |
239 | 1180 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon() | 1182 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon( |
240 | 1183 | version=0) | ||
241 | 1181 | 1184 | ||
242 | 1182 | # discharge the test macaroon | 1185 | # discharge the test macaroon |
243 | 1183 | self.account = self.factory.make_account() | 1186 | self.account = self.factory.make_account() |
244 | @@ -1196,7 +1199,7 @@ | |||
245 | 1196 | 1199 | ||
246 | 1197 | def test_macaroons_dont_verify_ok(self): | 1200 | def test_macaroons_dont_verify_ok(self): |
247 | 1198 | # just get *another* discharge so it's not for the same root macaroon | 1201 | # just get *another* discharge so it's not for the same root macaroon |
249 | 1199 | other_root, _, _ = self.build_macaroon() | 1202 | other_root, _, _ = self.build_macaroon(version=0) |
250 | 1200 | other_discharge = build_discharge_macaroon_from_root( | 1203 | other_discharge = build_discharge_macaroon_from_root( |
251 | 1201 | self.account, other_root.serialize()) | 1204 | self.account, other_root.serialize()) |
252 | 1202 | self.assertRaises(AuthenticationError, refresh_macaroons, | 1205 | self.assertRaises(AuthenticationError, refresh_macaroons, |
253 | @@ -1297,7 +1300,7 @@ | |||
254 | 1297 | 1300 | ||
255 | 1298 | def setUp(self): | 1301 | def setUp(self): |
256 | 1299 | super(MacaroonRefreshTestCase, self).setUp() | 1302 | super(MacaroonRefreshTestCase, self).setUp() |
258 | 1300 | root_macaroon, _, self.random_key = self.build_macaroon() | 1303 | root_macaroon, _, self.random_key = self.build_macaroon(version=1) |
259 | 1301 | (caveat,) = [c for c in root_macaroon.third_party_caveats() | 1304 | (caveat,) = [c for c in root_macaroon.third_party_caveats() |
260 | 1302 | if c.location == settings.MACAROON_SERVICE_LOCATION] | 1305 | if c.location == settings.MACAROON_SERVICE_LOCATION] |
261 | 1303 | 1306 | ||
262 | 1304 | 1307 | ||
263 | === modified file 'src/identityprovider/tests/test_forms.py' | |||
264 | --- src/identityprovider/tests/test_forms.py 2016-05-05 11:39:43 +0000 | |||
265 | +++ src/identityprovider/tests/test_forms.py 2016-05-11 17:42:55 +0000 | |||
266 | @@ -45,7 +45,6 @@ | |||
267 | 45 | UserAttribsRequestForm, | 45 | UserAttribsRequestForm, |
268 | 46 | tos_error, | 46 | tos_error, |
269 | 47 | ) | 47 | ) |
270 | 48 | from identityprovider.macaroon import MacaroonRequest | ||
271 | 49 | from identityprovider.models import Account, AccountPassword, OpenIDRPConfig | 48 | from identityprovider.models import Account, AccountPassword, OpenIDRPConfig |
272 | 50 | from identityprovider.models.const import AccountStatus, EmailStatus | 49 | from identityprovider.models.const import AccountStatus, EmailStatus |
273 | 51 | from identityprovider.tests import DEFAULT_USER_PASSWORD | 50 | from identityprovider.tests import DEFAULT_USER_PASSWORD |
274 | @@ -1131,71 +1130,71 @@ | |||
275 | 1131 | def setUp(self): | 1130 | def setUp(self): |
276 | 1132 | super(MacaroonRequestFormTestCase, self).setUp() | 1131 | super(MacaroonRequestFormTestCase, self).setUp() |
277 | 1133 | self.account = self.factory.make_account() | 1132 | self.account = self.factory.make_account() |
278 | 1133 | self.trust_root = 'http://localhost/' | ||
279 | 1134 | self.rpconfig = OpenIDRPConfig.objects.create( | 1134 | self.rpconfig = OpenIDRPConfig.objects.create( |
281 | 1135 | trust_root='http://localhost/', description="Some description") | 1135 | trust_root=self.trust_root, description="Some description") |
282 | 1136 | 1136 | ||
283 | 1137 | def test_field_for_trusted_site(self): | 1137 | def test_field_for_trusted_site(self): |
284 | 1138 | """The server always returns discharge macaroons to trusted sites, | 1138 | """The server always returns discharge macaroons to trusted sites, |
285 | 1139 | regardless of the state of the checkbox in the UI. | 1139 | regardless of the state of the checkbox in the UI. |
286 | 1140 | """ | 1140 | """ |
289 | 1141 | root_macaroon, _, _ = self.build_macaroon() | 1141 | macaroon_request = self.build_macaroon_request() |
288 | 1142 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
290 | 1143 | form = MacaroonRequestForm( | 1142 | form = MacaroonRequestForm( |
291 | 1144 | request=self._get_request_with_post_args(), | 1143 | request=self._get_request_with_post_args(), |
293 | 1145 | macaroon_request=macaroon_request, rpconfig=self.rpconfig) | 1144 | macaroon_request=macaroon_request, rpconfig=self.rpconfig, |
294 | 1145 | trust_root=self.trust_root) | ||
295 | 1146 | self.assertIn('macaroon', form.data_approved_for_request) | 1146 | self.assertIn('macaroon', form.data_approved_for_request) |
296 | 1147 | 1147 | ||
297 | 1148 | def test_checked_field_for_untrusted_site(self): | 1148 | def test_checked_field_for_untrusted_site(self): |
298 | 1149 | """The server returns discharge macaroons to untrusted sites when | 1149 | """The server returns discharge macaroons to untrusted sites when |
299 | 1150 | the user checks the checkbox in the UI. | 1150 | the user checks the checkbox in the UI. |
300 | 1151 | """ | 1151 | """ |
303 | 1152 | root_macaroon, _, _ = self.build_macaroon() | 1152 | macaroon_request = self.build_macaroon_request() |
302 | 1153 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
304 | 1154 | form = MacaroonRequestForm( | 1153 | form = MacaroonRequestForm( |
305 | 1155 | request=self._get_request_with_post_args(macaroon='macaroon'), | 1154 | request=self._get_request_with_post_args(macaroon='macaroon'), |
307 | 1156 | macaroon_request=macaroon_request, rpconfig=None) | 1155 | macaroon_request=macaroon_request, rpconfig=None, |
308 | 1156 | trust_root=self.trust_root) | ||
309 | 1157 | self.assertIn('macaroon', form.data_approved_for_request) | 1157 | self.assertIn('macaroon', form.data_approved_for_request) |
310 | 1158 | 1158 | ||
311 | 1159 | def test_unchecked_field_for_untrusted_site(self): | 1159 | def test_unchecked_field_for_untrusted_site(self): |
312 | 1160 | """The server does not return discharge macaroons to untrusted sites | 1160 | """The server does not return discharge macaroons to untrusted sites |
313 | 1161 | when the user does not check the checkbox in the UI. | 1161 | when the user does not check the checkbox in the UI. |
314 | 1162 | """ | 1162 | """ |
317 | 1163 | root_macaroon, _, _ = self.build_macaroon() | 1163 | macaroon_request = self.build_macaroon_request() |
316 | 1164 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
318 | 1165 | form = MacaroonRequestForm( | 1164 | form = MacaroonRequestForm( |
319 | 1166 | request=self._get_request_with_post_args(), | 1165 | request=self._get_request_with_post_args(), |
321 | 1167 | macaroon_request=macaroon_request, rpconfig=None) | 1166 | macaroon_request=macaroon_request, rpconfig=None, |
322 | 1167 | trust_root=self.trust_root) | ||
323 | 1168 | self.assertNotIn('macaroon', form.data_approved_for_request) | 1168 | self.assertNotIn('macaroon', form.data_approved_for_request) |
324 | 1169 | 1169 | ||
325 | 1170 | def test_checkbox_status_for_trusted_site(self): | 1170 | def test_checkbox_status_for_trusted_site(self): |
326 | 1171 | """Checkboxes are always checked if the site is trusted.""" | 1171 | """Checkboxes are always checked if the site is trusted.""" |
329 | 1172 | root_macaroon, _, _ = self.build_macaroon() | 1172 | macaroon_request = self.build_macaroon_request() |
328 | 1173 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
330 | 1174 | form = MacaroonRequestForm( | 1173 | form = MacaroonRequestForm( |
331 | 1175 | request=self._get_request_with_post_args(), | 1174 | request=self._get_request_with_post_args(), |
333 | 1176 | macaroon_request=macaroon_request, rpconfig=self.rpconfig) | 1175 | macaroon_request=macaroon_request, rpconfig=self.rpconfig, |
334 | 1176 | trust_root=self.trust_root) | ||
335 | 1177 | self.assertTrue(form.check_test('macaroon')(True)) | 1177 | self.assertTrue(form.check_test('macaroon')(True)) |
336 | 1178 | 1178 | ||
337 | 1179 | def test_checkbox_status_for_untrusted_site(self): | 1179 | def test_checkbox_status_for_untrusted_site(self): |
338 | 1180 | """Checkboxes are checked by default if the site is untrusted.""" | 1180 | """Checkboxes are checked by default if the site is untrusted.""" |
341 | 1181 | root_macaroon, _, _ = self.build_macaroon() | 1181 | macaroon_request = self.build_macaroon_request() |
340 | 1182 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
342 | 1183 | form = MacaroonRequestForm( | 1182 | form = MacaroonRequestForm( |
343 | 1184 | request=self._get_request_with_post_args(), | 1183 | request=self._get_request_with_post_args(), |
345 | 1185 | macaroon_request=macaroon_request, rpconfig=None) | 1184 | macaroon_request=macaroon_request, rpconfig=None, |
346 | 1185 | trust_root=self.trust_root) | ||
347 | 1186 | self.assertTrue(form.check_test('macaroon')(True)) | 1186 | self.assertTrue(form.check_test('macaroon')(True)) |
348 | 1187 | 1187 | ||
349 | 1188 | def test_checkbox_status_for_untrusted_site_with_approved_data(self): | 1188 | def test_checkbox_status_for_untrusted_site_with_approved_data(self): |
350 | 1189 | """Checkboxes respect user preferences on untrusted sites where | 1189 | """Checkboxes respect user preferences on untrusted sites where |
351 | 1190 | available. | 1190 | available. |
352 | 1191 | """ | 1191 | """ |
355 | 1192 | root_macaroon, _, _ = self.build_macaroon() | 1192 | macaroon_request = self.build_macaroon_request() |
354 | 1193 | macaroon_request = MacaroonRequest(root_macaroon.serialize()) | ||
356 | 1194 | approved_data = { | 1193 | approved_data = { |
357 | 1195 | 'requested': ['macaroon'], | 1194 | 'requested': ['macaroon'], |
358 | 1196 | 'approved': []} | 1195 | 'approved': []} |
359 | 1197 | form = MacaroonRequestForm( | 1196 | form = MacaroonRequestForm( |
360 | 1198 | request=self._get_request_with_post_args(), | 1197 | request=self._get_request_with_post_args(), |
361 | 1199 | macaroon_request=macaroon_request, rpconfig=None, | 1198 | macaroon_request=macaroon_request, rpconfig=None, |
363 | 1200 | approved_data=approved_data) | 1199 | trust_root=self.trust_root, approved_data=approved_data) |
364 | 1201 | self.assertFalse(form.check_test('macaroon')(True)) | 1200 | self.assertFalse(form.check_test('macaroon')(True)) |
365 | 1202 | 1201 | ||
366 | === modified file 'src/identityprovider/tests/test_macaroon.py' | |||
367 | --- src/identityprovider/tests/test_macaroon.py 2016-05-05 11:39:43 +0000 | |||
368 | +++ src/identityprovider/tests/test_macaroon.py 2016-05-11 17:42:55 +0000 | |||
369 | @@ -45,19 +45,17 @@ | |||
370 | 45 | def setUp(self): | 45 | def setUp(self): |
371 | 46 | super(MacaroonRequestTestCase, self).setUp() | 46 | super(MacaroonRequestTestCase, self).setUp() |
372 | 47 | 47 | ||
378 | 48 | self.root_macaroon, self.macaroon_random_key, _ = self.build_macaroon() | 48 | root_macaroon, _, _ = self.build_macaroon(version=1) |
379 | 49 | self.req = MacaroonRequest(self.root_macaroon.serialize()) | 49 | sso_caveat = self.get_sso_caveat(root_macaroon) |
380 | 50 | 50 | self.caveat_id = sso_caveat.caveat_id | |
381 | 51 | def assertMacaroonsEqual(self, expected, observed): | 51 | self.req = MacaroonRequest(self.caveat_id) |
377 | 52 | self.assertEqual(expected.serialize(), observed.serialize()) | ||
382 | 53 | 52 | ||
383 | 54 | def test_init(self): | 53 | def test_init(self): |
384 | 55 | req = MacaroonRequest() | 54 | req = MacaroonRequest() |
387 | 56 | self.assertIsNone(req.root_macaroon_raw) | 55 | self.assertIsNone(req.caveat_id) |
386 | 57 | self.assertIsNone(req.root_macaroon) | ||
388 | 58 | self.assertEqual(MACAROON_NS, req.ns_uri) | 56 | self.assertEqual(MACAROON_NS, req.ns_uri) |
389 | 59 | 57 | ||
391 | 60 | self.assertMacaroonsEqual(self.root_macaroon, self.req.root_macaroon) | 58 | self.assertEqual(self.caveat_id, self.req.caveat_id) |
392 | 61 | 59 | ||
393 | 62 | def test_fromOpenIDRequest(self): | 60 | def test_fromOpenIDRequest(self): |
394 | 63 | params = {'openid.mode': 'checkid_setup', | 61 | params = {'openid.mode': 'checkid_setup', |
395 | @@ -68,8 +66,7 @@ | |||
396 | 68 | openid_server = server._get_openid_server(request) | 66 | openid_server = server._get_openid_server(request) |
397 | 69 | orequest = openid_server.decodeRequest(params) | 67 | orequest = openid_server.decodeRequest(params) |
398 | 70 | req = MacaroonRequest.fromOpenIDRequest(orequest) | 68 | req = MacaroonRequest.fromOpenIDRequest(orequest) |
401 | 71 | self.assertIsNone(req.root_macaroon_raw) | 69 | self.assertIsNone(req.caveat_id) |
400 | 72 | self.assertIsNone(req.root_macaroon) | ||
402 | 73 | self.assertEqual(MACAROON_NS, req.ns_uri) | 70 | self.assertEqual(MACAROON_NS, req.ns_uri) |
403 | 74 | 71 | ||
404 | 75 | def test_fromOpenIDRequest_with_root(self): | 72 | def test_fromOpenIDRequest_with_root(self): |
405 | @@ -77,22 +74,21 @@ | |||
406 | 77 | 'openid.trust_root': 'http://localhost/', | 74 | 'openid.trust_root': 'http://localhost/', |
407 | 78 | 'openid.return_to': 'http://localhost/', | 75 | 'openid.return_to': 'http://localhost/', |
408 | 79 | 'openid.identity': IDENTIFIER_SELECT, | 76 | 'openid.identity': IDENTIFIER_SELECT, |
410 | 80 | 'openid.macaroon.root': self.root_macaroon.serialize()} | 77 | 'openid.macaroon.caveat_id': self.caveat_id} |
411 | 81 | request = self.factory.make_request(**params) | 78 | request = self.factory.make_request(**params) |
412 | 82 | openid_server = server._get_openid_server(request) | 79 | openid_server = server._get_openid_server(request) |
413 | 83 | orequest = openid_server.decodeRequest(params) | 80 | orequest = openid_server.decodeRequest(params) |
414 | 84 | req = MacaroonRequest.fromOpenIDRequest(orequest) | 81 | req = MacaroonRequest.fromOpenIDRequest(orequest) |
416 | 85 | self.assertMacaroonsEqual(self.root_macaroon, req.root_macaroon) | 82 | self.assertEqual(self.caveat_id, req.caveat_id) |
417 | 86 | self.assertEqual(MACAROON_NS, req.ns_uri) | 83 | self.assertEqual(MACAROON_NS, req.ns_uri) |
418 | 87 | 84 | ||
419 | 88 | def test_parseExtensionArgs(self): | 85 | def test_parseExtensionArgs(self): |
420 | 89 | req = MacaroonRequest() | 86 | req = MacaroonRequest() |
424 | 90 | req.parseExtensionArgs( | 87 | req.parseExtensionArgs({'caveat_id': self.caveat_id}) |
425 | 91 | {'root': self.root_macaroon.serialize()}) | 88 | self.assertEqual(self.caveat_id, req.caveat_id) |
423 | 92 | self.assertMacaroonsEqual(self.root_macaroon, req.root_macaroon) | ||
426 | 93 | 89 | ||
427 | 94 | def test_getExtensionArgs(self): | 90 | def test_getExtensionArgs(self): |
429 | 95 | expected = {'root': self.root_macaroon.serialize()} | 91 | expected = {'caveat_id': self.caveat_id} |
430 | 96 | self.assertEqual(expected, self.req.getExtensionArgs()) | 92 | self.assertEqual(expected, self.req.getExtensionArgs()) |
431 | 97 | 93 | ||
432 | 98 | def test_getExtensionArgs_no_root(self): | 94 | def test_getExtensionArgs_no_root(self): |
433 | 99 | 95 | ||
434 | === modified file 'src/identityprovider/tests/test_views_server.py' | |||
435 | --- src/identityprovider/tests/test_views_server.py 2016-05-10 00:44:27 +0000 | |||
436 | +++ src/identityprovider/tests/test_views_server.py 2016-05-11 17:42:55 +0000 | |||
437 | @@ -227,7 +227,7 @@ | |||
438 | 227 | self._test_auto_auth(sreg=['fullname']) | 227 | self._test_auto_auth(sreg=['fullname']) |
439 | 228 | 228 | ||
440 | 229 | def test_handle_user_response_auto_auth_discharge_macaroon(self): | 229 | def test_handle_user_response_auto_auth_discharge_macaroon(self): |
442 | 230 | root_macaroon, macaroon_random_key, _ = self.build_macaroon() | 230 | root_macaroon, macaroon_random_key, _ = self.build_macaroon(version=1) |
443 | 231 | # Add padding to force a POST after signing. We don't know exactly | 231 | # Add padding to force a POST after signing. We don't know exactly |
444 | 232 | # how long the serialized discharge macaroon will be yet, but it | 232 | # how long the serialized discharge macaroon will be yet, but it |
445 | 233 | # will probably be at least 1024 bytes. | 233 | # will probably be at least 1024 bytes. |
446 | @@ -290,9 +290,10 @@ | |||
447 | 290 | for k, v in expected_values.iteritems() | 290 | for k, v in expected_values.iteritems() |
448 | 291 | if k not in sreg or v is None] | 291 | if k not in sreg or v is None] |
449 | 292 | if root_macaroon and macaroon_key: | 292 | if root_macaroon and macaroon_key: |
450 | 293 | sso_caveat = self.get_sso_caveat(root_macaroon) | ||
451 | 293 | self.params.update({ | 294 | self.params.update({ |
452 | 294 | 'openid.ns.macaroon': MACAROON_NS, | 295 | 'openid.ns.macaroon': MACAROON_NS, |
454 | 295 | 'openid.macaroon.root': root_macaroon.serialize(), | 296 | 'openid.macaroon.caveat_id': sso_caveat.caveat_id, |
455 | 296 | }) | 297 | }) |
456 | 297 | unexpected_fields.remove('openid.macaroon.discharge') | 298 | unexpected_fields.remove('openid.macaroon.discharge') |
457 | 298 | response = self.client.post(self.url, self.params) | 299 | response = self.client.post(self.url, self.params) |
458 | @@ -314,6 +315,8 @@ | |||
459 | 314 | verifier.satisfy_general(lambda caveat: True) | 315 | verifier.satisfy_general(lambda caveat: True) |
460 | 315 | discharge_macaroon = Macaroon.deserialize( | 316 | discharge_macaroon = Macaroon.deserialize( |
461 | 316 | forms[0].fields['openid.macaroon.discharge']) | 317 | forms[0].fields['openid.macaroon.discharge']) |
462 | 318 | discharge_macaroon = root_macaroon.prepare_for_request( | ||
463 | 319 | discharge_macaroon) | ||
464 | 317 | self.assertRaises( | 320 | self.assertRaises( |
465 | 318 | MacaroonUnmetCaveatException, verifier.verify, | 321 | MacaroonUnmetCaveatException, verifier.verify, |
466 | 319 | root_macaroon, macaroon_key, []) | 322 | root_macaroon, macaroon_key, []) |
467 | @@ -1066,10 +1069,11 @@ | |||
468 | 1066 | # make sure rpconfig is set to auto authorize | 1069 | # make sure rpconfig is set to auto authorize |
469 | 1067 | OpenIDRPConfig.objects.create( | 1070 | OpenIDRPConfig.objects.create( |
470 | 1068 | trust_root='http://localhost/', auto_authorize=True) | 1071 | trust_root='http://localhost/', auto_authorize=True) |
472 | 1069 | root_macaroon, macaroon_random_key, _ = self.build_macaroon() | 1072 | root_macaroon, macaroon_random_key, _ = self.build_macaroon(version=1) |
473 | 1073 | sso_caveat = self.get_sso_caveat(root_macaroon) | ||
474 | 1070 | param_overrides = { | 1074 | param_overrides = { |
475 | 1071 | 'openid.ns.macaroon': MACAROON_NS, | 1075 | 'openid.ns.macaroon': MACAROON_NS, |
477 | 1072 | 'openid.macaroon.root': root_macaroon.serialize(), | 1076 | 'openid.macaroon.caveat_id': sso_caveat.caveat_id, |
478 | 1073 | } | 1077 | } |
479 | 1074 | self._prepare_openid_token(param_overrides=param_overrides) | 1078 | self._prepare_openid_token(param_overrides=param_overrides) |
480 | 1075 | response = self.client.post(self.url) | 1079 | response = self.client.post(self.url) |
481 | @@ -1086,14 +1090,17 @@ | |||
482 | 1086 | self.assertRaises( | 1090 | self.assertRaises( |
483 | 1087 | MacaroonUnmetCaveatException, verifier.verify, | 1091 | MacaroonUnmetCaveatException, verifier.verify, |
484 | 1088 | root_macaroon, macaroon_random_key, []) | 1092 | root_macaroon, macaroon_random_key, []) |
485 | 1093 | discharge_macaroon = root_macaroon.prepare_for_request( | ||
486 | 1094 | discharge_macaroon) | ||
487 | 1089 | self.assertTrue(verifier.verify( | 1095 | self.assertTrue(verifier.verify( |
488 | 1090 | root_macaroon, macaroon_random_key, [discharge_macaroon])) | 1096 | root_macaroon, macaroon_random_key, [discharge_macaroon])) |
489 | 1091 | 1097 | ||
490 | 1092 | def test_state_of_checkboxes_and_data_formats_macaroon(self): | 1098 | def test_state_of_checkboxes_and_data_formats_macaroon(self): |
492 | 1093 | root_macaroon, _, _ = self.build_macaroon() | 1099 | root_macaroon, _, _ = self.build_macaroon(version=1) |
493 | 1100 | sso_caveat = self.get_sso_caveat(root_macaroon) | ||
494 | 1094 | param_overrides = { | 1101 | param_overrides = { |
495 | 1095 | 'openid.ns.macaroon': MACAROON_NS, | 1102 | 'openid.ns.macaroon': MACAROON_NS, |
497 | 1096 | 'openid.macaroon.root': root_macaroon.serialize(), | 1103 | 'openid.macaroon.caveat_id': sso_caveat.caveat_id, |
498 | 1097 | } | 1104 | } |
499 | 1098 | self._prepare_openid_token(param_overrides=param_overrides) | 1105 | self._prepare_openid_token(param_overrides=param_overrides) |
500 | 1099 | response = self.client.post(self.url) | 1106 | response = self.client.post(self.url) |
501 | @@ -1101,7 +1108,7 @@ | |||
502 | 1101 | # This field is checked regardless of whether a site is trusted. | 1108 | # This field is checked regardless of whether a site is trusted. |
503 | 1102 | self._test_optional_trusted_field( | 1109 | self._test_optional_trusted_field( |
504 | 1103 | dom, field='macaroon', | 1110 | dom, field='macaroon', |
506 | 1104 | value='Service authorization for The store ;)') | 1111 | value='Service authorization for http://localhost/') |
507 | 1105 | 1112 | ||
508 | 1106 | 1113 | ||
509 | 1107 | class DecideUserUnverifiedTestCase(DecideBaseTestCase): | 1114 | class DecideUserUnverifiedTestCase(DecideBaseTestCase): |
510 | @@ -1865,9 +1872,10 @@ | |||
511 | 1865 | if with_teams: | 1872 | if with_teams: |
512 | 1866 | params['openid.lp.query_membership'] = 'ubuntu-team' | 1873 | params['openid.lp.query_membership'] = 'ubuntu-team' |
513 | 1867 | if with_macaroon: | 1874 | if with_macaroon: |
515 | 1868 | root_macaroon, _, _ = self.build_macaroon() | 1875 | root_macaroon, _, _ = self.build_macaroon(version=1) |
516 | 1876 | sso_caveat = self.get_sso_caveat(root_macaroon) | ||
517 | 1869 | params['openid.ns.macaroon'] = MACAROON_NS | 1877 | params['openid.ns.macaroon'] = MACAROON_NS |
519 | 1870 | params['openid.macaroon.root'] = root_macaroon.serialize() | 1878 | params['openid.macaroon.caveat_id'] = sso_caveat.caveat_id |
520 | 1871 | provider_url = get_provider_url(request) | 1879 | provider_url = get_provider_url(request) |
521 | 1872 | openid_server = server._get_openid_server(provider_url) | 1880 | openid_server = server._get_openid_server(provider_url) |
522 | 1873 | return openid_server.decodeRequest(params) | 1881 | return openid_server.decodeRequest(params) |
523 | 1874 | 1882 | ||
524 | === modified file 'src/identityprovider/tests/utils.py' | |||
525 | --- src/identityprovider/tests/utils.py 2016-05-10 16:12:20 +0000 | |||
526 | +++ src/identityprovider/tests/utils.py 2016-05-11 17:42:55 +0000 | |||
527 | @@ -33,6 +33,7 @@ | |||
528 | 33 | # import signals to ensure all the handlers are connected and tests use the | 33 | # import signals to ensure all the handlers are connected and tests use the |
529 | 34 | # same logic as prod systems | 34 | # same logic as prod systems |
530 | 35 | from identityprovider import signed, signals # noqa | 35 | from identityprovider import signed, signals # noqa |
531 | 36 | from identityprovider.macaroon import MacaroonRequest | ||
532 | 36 | from identityprovider.tests import DEFAULT_USER_PASSWORD | 37 | from identityprovider.tests import DEFAULT_USER_PASSWORD |
533 | 37 | from identityprovider.tests.factory import SSOObjectFactory | 38 | from identityprovider.tests.factory import SSOObjectFactory |
534 | 38 | from identityprovider.utils import generate_random_string | 39 | from identityprovider.utils import generate_random_string |
535 | @@ -181,7 +182,7 @@ | |||
536 | 181 | self.addCleanup(p.disable) | 182 | self.addCleanup(p.disable) |
537 | 182 | return test_rsa_priv_key, test_rsa_pub_key | 183 | return test_rsa_priv_key, test_rsa_pub_key |
538 | 183 | 184 | ||
540 | 184 | def build_macaroon(self, service_location=None): | 185 | def build_macaroon(self, service_location=None, version=1): |
541 | 185 | if service_location is None: | 186 | if service_location is None: |
542 | 186 | service_location = settings.MACAROON_SERVICE_LOCATION | 187 | service_location = settings.MACAROON_SERVICE_LOCATION |
543 | 187 | test_rsa_priv_key, test_rsa_pub_key = self.setup_key_pair() | 188 | test_rsa_priv_key, test_rsa_pub_key = self.setup_key_pair() |
544 | @@ -194,16 +195,37 @@ | |||
545 | 194 | identifier='A test macaroon', | 195 | identifier='A test macaroon', |
546 | 195 | ) | 196 | ) |
547 | 196 | random_key = binascii.hexlify(os.urandom(32)) | 197 | random_key = binascii.hexlify(os.urandom(32)) |
554 | 197 | info = { | 198 | if version == 0: |
555 | 198 | 'roothash': macaroon_random_key, | 199 | info = { |
556 | 199 | '3rdparty': random_key, | 200 | 'roothash': macaroon_random_key, |
557 | 200 | } | 201 | '3rdparty': random_key, |
558 | 201 | info_encrypted = base64.b64encode( | 202 | } |
559 | 202 | test_rsa_pub_key.encrypt(json.dumps(info), 32)[0]) | 203 | payload = base64.b64encode( |
560 | 204 | test_rsa_pub_key.encrypt(json.dumps(info), 32)[0]) | ||
561 | 205 | else: | ||
562 | 206 | payload = json.dumps({ | ||
563 | 207 | 'version': 1, | ||
564 | 208 | 'secret': base64.b64encode( | ||
565 | 209 | test_rsa_pub_key.encrypt(random_key, 32)[0]), | ||
566 | 210 | }) | ||
567 | 203 | root_macaroon.add_third_party_caveat( | 211 | root_macaroon.add_third_party_caveat( |
569 | 204 | service_location, random_key, info_encrypted) | 212 | service_location, random_key, payload) |
570 | 205 | return root_macaroon, macaroon_random_key, random_key | 213 | return root_macaroon, macaroon_random_key, random_key |
571 | 206 | 214 | ||
572 | 215 | def get_sso_caveat(self, macaroon, service_location=None): | ||
573 | 216 | """Extract the SSO caveat from a macaroon.""" | ||
574 | 217 | if service_location is None: | ||
575 | 218 | service_location = settings.MACAROON_SERVICE_LOCATION | ||
576 | 219 | (sso_caveat,) = [c for c in macaroon.third_party_caveats() | ||
577 | 220 | if c.location == service_location] | ||
578 | 221 | return sso_caveat | ||
579 | 222 | |||
580 | 223 | def build_macaroon_request(self): | ||
581 | 224 | """Build a macaroon and a MacaroonRequest to discharge it.""" | ||
582 | 225 | root_macaroon, _, _ = self.build_macaroon(version=1) | ||
583 | 226 | sso_caveat = self.get_sso_caveat(root_macaroon) | ||
584 | 227 | return MacaroonRequest(sso_caveat.caveat_id) | ||
585 | 228 | |||
586 | 207 | def use_fixture(self, fixture): | 229 | def use_fixture(self, fixture): |
587 | 208 | """Set up 'fixture' to be used with the current test. | 230 | """Set up 'fixture' to be used with the current test. |
588 | 209 | 231 | ||
589 | 210 | 232 | ||
590 | === modified file 'src/identityprovider/views/server.py' | |||
591 | --- src/identityprovider/views/server.py 2016-05-05 11:39:43 +0000 | |||
592 | +++ src/identityprovider/views/server.py 2016-05-11 17:42:55 +0000 | |||
593 | @@ -42,7 +42,7 @@ | |||
594 | 42 | from openid.yadis.constants import YADIS_HEADER_NAME | 42 | from openid.yadis.constants import YADIS_HEADER_NAME |
595 | 43 | 43 | ||
596 | 44 | from identityprovider import signed | 44 | from identityprovider import signed |
598 | 45 | from identityprovider.auth import build_discharge_macaroon_from_root | 45 | from identityprovider.auth import build_discharge_macaroon |
599 | 46 | from identityprovider.const import ( | 46 | from identityprovider.const import ( |
600 | 47 | AX_DATA_FIELDS, | 47 | AX_DATA_FIELDS, |
601 | 48 | MACAROON_NS, | 48 | MACAROON_NS, |
602 | @@ -321,7 +321,7 @@ | |||
603 | 321 | teams_form = TeamsRequestForm(request, teams_request, rpconfig, | 321 | teams_form = TeamsRequestForm(request, teams_request, rpconfig, |
604 | 322 | approved_data=approved_data.get('teams')) | 322 | approved_data=approved_data.get('teams')) |
605 | 323 | macaroon_form = MacaroonRequestForm( | 323 | macaroon_form = MacaroonRequestForm( |
607 | 324 | request, macaroon_request, rpconfig, | 324 | request, macaroon_request, rpconfig, orequest.trust_root, |
608 | 325 | approved_data=approved_data.get('macaroon')) | 325 | approved_data=approved_data.get('macaroon')) |
609 | 326 | context = { | 326 | context = { |
610 | 327 | 'account': request.user, | 327 | 'account': request.user, |
611 | @@ -637,9 +637,10 @@ | |||
612 | 637 | 'approved': teams_form.teams_approved_by_user} | 637 | 'approved': teams_form.teams_approved_by_user} |
613 | 638 | 638 | ||
614 | 639 | macaroon_args = orequest.message.getArgs(MACAROON_NS) | 639 | macaroon_args = orequest.message.getArgs(MACAROON_NS) |
616 | 640 | if macaroon_args.get('root'): | 640 | if macaroon_args.get('caveat_id'): |
617 | 641 | macaroon_form = MacaroonRequestForm( | 641 | macaroon_form = MacaroonRequestForm( |
619 | 642 | request, MacaroonRequest.fromOpenIDRequest(orequest), rpconfig) | 642 | request, MacaroonRequest.fromOpenIDRequest(orequest), rpconfig, |
620 | 643 | orequest.trust_root) | ||
621 | 643 | approved_data['macaroon'] = { | 644 | approved_data['macaroon'] = { |
622 | 644 | 'requested': macaroon_form.data.keys(), | 645 | 'requested': macaroon_form.data.keys(), |
623 | 645 | 'approved': macaroon_form.data_approved_for_request.keys()} | 646 | 'approved': macaroon_form.data_approved_for_request.keys()} |
624 | @@ -767,10 +768,11 @@ | |||
625 | 767 | """Add discharge macaroon if requested and approved.""" | 768 | """Add discharge macaroon if requested and approved.""" |
626 | 768 | macaroon_request = MacaroonRequest.fromOpenIDRequest(openid_request) | 769 | macaroon_request = MacaroonRequest.fromOpenIDRequest(openid_request) |
627 | 769 | rpconfig = utils.get_rpconfig(openid_request.trust_root) | 770 | rpconfig = utils.get_rpconfig(openid_request.trust_root) |
629 | 770 | form = MacaroonRequestForm(request, macaroon_request, rpconfig) | 771 | form = MacaroonRequestForm( |
630 | 772 | request, macaroon_request, rpconfig, openid_request.trust_root) | ||
631 | 771 | if form.data_approved_for_request: | 773 | if form.data_approved_for_request: |
634 | 772 | discharge_macaroon = build_discharge_macaroon_from_root( | 774 | discharge_macaroon = build_discharge_macaroon( |
635 | 773 | request.user, macaroon_request.root_macaroon_raw) | 775 | request.user, macaroon_request.caveat_id) |
636 | 774 | macaroon_response = MacaroonResponse.extractResponse( | 776 | macaroon_response = MacaroonResponse.extractResponse( |
637 | 775 | macaroon_request, discharge_macaroon.serialize()) | 777 | macaroon_request, discharge_macaroon.serialize()) |
638 | 776 | openid_response.addExtension(macaroon_response) | 778 | openid_response.addExtension(macaroon_response) |
639 | 777 | 779 | ||
640 | === modified file 'src/webui/views/consumer.py' | |||
641 | --- src/webui/views/consumer.py 2016-04-11 08:57:57 +0000 | |||
642 | +++ src/webui/views/consumer.py 2016-05-11 17:42:55 +0000 | |||
643 | @@ -171,24 +171,13 @@ | |||
644 | 171 | auth_request.addExtension(teams_request_from_string(req_teams)) | 171 | auth_request.addExtension(teams_request_from_string(req_teams)) |
645 | 172 | 172 | ||
646 | 173 | if request.POST.get('macaroon', False): | 173 | if request.POST.get('macaroon', False): |
647 | 174 | macaroon_random_key = binascii.hexlify(os.urandom(32)) | ||
648 | 175 | root_macaroon = Macaroon( | ||
649 | 176 | location='Test consumer', | ||
650 | 177 | key=macaroon_random_key, | ||
651 | 178 | identifier='A test macaroon', | ||
652 | 179 | ) | ||
653 | 180 | random_key = binascii.hexlify(os.urandom(32)) | 174 | random_key = binascii.hexlify(os.urandom(32)) |
654 | 181 | info = { | ||
655 | 182 | 'roothash': macaroon_random_key, | ||
656 | 183 | '3rdparty': random_key, | ||
657 | 184 | } | ||
658 | 185 | pubkey = settings.CRYPTO_SSO_PRIVKEY.publickey() | 175 | pubkey = settings.CRYPTO_SSO_PRIVKEY.publickey() |
665 | 186 | info_encrypted = base64.b64encode( | 176 | caveat_id = json.dumps({ |
666 | 187 | pubkey.encrypt(json.dumps(info), 32)[0]) | 177 | 'version': 1, |
667 | 188 | root_macaroon.add_third_party_caveat( | 178 | 'secret': base64.b64encode(pubkey.encrypt(random_key, 32)[0]), |
668 | 189 | settings.MACAROON_SERVICE_LOCATION, random_key, info_encrypted) | 179 | }) |
669 | 190 | macaroon_request = macaroon.MacaroonRequest( | 180 | macaroon_request = macaroon.MacaroonRequest(caveat_id) |
664 | 191 | root_macaroon.serialize()) | ||
670 | 192 | auth_request.addExtension(macaroon_request) | 181 | auth_request.addExtension(macaroon_request) |
671 | 193 | 182 | ||
672 | 194 | # Compute the trust root and return URL values to build the | 183 | # Compute the trust root and return URL values to build the |
Much nicer. Thanks!