Merge lp:~canonical-isd-hackers/canonical-identity-provider/ssoclient into lp:~ubuntuone-pqm-team/canonical-identity-provider/ssoclient
- ssoclient
- Merge into ssoclient
Proposed by
Natalia Bidart
Status: | Merged |
---|---|
Approved by: | Martin Albisetti |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9 |
Proposed branch: | lp:~canonical-isd-hackers/canonical-identity-provider/ssoclient |
Merge into: | lp:~ubuntuone-pqm-team/canonical-identity-provider/ssoclient |
Diff against target: |
548 lines (+279/-102) 3 files modified
ssoclient/tests/test_v2.py (+229/-86) ssoclient/v2/client.py (+46/-16) ssoclient/v2/errors.py (+4/-0) |
To merge this branch: | bzr merge lp:~canonical-isd-hackers/canonical-identity-provider/ssoclient |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Albisetti (community) | Approve | ||
Review via email: mp+180909@code.launchpad.net |
Commit message
- Bump stable branch to revno 9 from devel branch.
Description of the change
To post a comment you must log in.
Revision history for this message
Martin Albisetti (beuno) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ssoclient/tests/test_v2.py' |
2 | --- ssoclient/tests/test_v2.py 2013-05-24 12:27:05 +0000 |
3 | +++ ssoclient/tests/test_v2.py 2013-08-19 16:45:04 +0000 |
4 | @@ -96,6 +96,29 @@ |
5 | super(V2ClientApiTestCase, self).setUp() |
6 | self.client = V2ApiClient('http://foo.com') |
7 | |
8 | + p = patch(REQUEST) |
9 | + self.mock_request = p.start() |
10 | + self.addCleanup(p.stop) |
11 | + |
12 | + p = patch('ssoclient.v2.client.OAuth1') |
13 | + self.mock_oauth = p.start() |
14 | + self.addCleanup(p.stop) |
15 | + |
16 | + self.credentials = dict( |
17 | + consumer_key='consumer_key', |
18 | + consumer_secret='consumer_secret', |
19 | + token_key='token_key', |
20 | + token_secret='token_secret', |
21 | + ) |
22 | + |
23 | + def assert_unicode_credentials(self, credentials): |
24 | + self.mock_oauth.assert_called_once_with( |
25 | + credentials['consumer_key'], credentials['consumer_secret'], |
26 | + credentials['token_key'], credentials['token_secret'], |
27 | + ) |
28 | + self.assertTrue(all(isinstance(val, unicode) for |
29 | + val in self.mock_oauth.call_args[0])) |
30 | + |
31 | |
32 | class RegisterV2ClientApiTestCase(V2ClientApiTestCase): |
33 | |
34 | @@ -106,42 +129,47 @@ |
35 | response.json.side_effect = ValueError |
36 | response.text = 'some error message' |
37 | |
38 | - with patch(REQUEST, return_value=response): |
39 | - with self.assertRaises(ExceptionClass) as ctx: |
40 | - self.client.login(email='blah') |
41 | + self.mock_request.return_value = response |
42 | + with self.assertRaises(ExceptionClass) as ctx: |
43 | + self.client.login(email='blah') |
44 | |
45 | if status_code >= 500: |
46 | self.assertIn('some error message', str(ctx.exception)) |
47 | |
48 | - @patch(REQUEST, return_value=mock_response(400, code="INVALID_DATA")) |
49 | - def test_register_invalid_data(self, mock_request): |
50 | + def test_register_invalid_data(self): |
51 | + self.mock_request.return_value = mock_response( |
52 | + 400, code="INVALID_DATA") |
53 | with self.assertRaises(errors.InvalidData): |
54 | self.client.register(email='blah') |
55 | |
56 | - @patch(REQUEST, return_value=mock_response(401, code="CAPTCHA_REQUIRED")) |
57 | - def test_register_captcha_required(self, mock_request): |
58 | + def test_register_captcha_required(self): |
59 | + self.mock_request.return_value = mock_response( |
60 | + 401, code="CAPTCHA_REQUIRED") |
61 | with self.assertRaises(errors.CaptchaRequired): |
62 | self.client.register(email='blah') |
63 | |
64 | - @patch(REQUEST, return_value=mock_response(403, code="CAPTCHA_FAILURE")) |
65 | - def test_register_captcha_failed(self, mock_request): |
66 | + def test_register_captcha_failed(self): |
67 | + self.mock_request.return_value = mock_response( |
68 | + 403, code="CAPTCHA_FAILURE") |
69 | with self.assertRaises(errors.CaptchaFailure): |
70 | self.client.register(email='blah') |
71 | |
72 | - @patch(REQUEST, return_value=mock_response(502, code="CAPTCHA_ERROR")) |
73 | - def test_register_captcha_error(self, mock_request): |
74 | + def test_register_captcha_error(self): |
75 | + self.mock_request.return_value = mock_response( |
76 | + 502, code="CAPTCHA_ERROR") |
77 | with self.assertRaises(errors.CaptchaError): |
78 | self.client.register(email='blah') |
79 | |
80 | - @patch(REQUEST, return_value=mock_response(409, code="ALREADY_REGISTERED")) |
81 | - def test_register_already_registered(self, mock_request): |
82 | + def test_register_already_registered(self): |
83 | + self.mock_request.return_value = mock_response( |
84 | + 409, code="ALREADY_REGISTERED") |
85 | with self.assertRaises(errors.AlreadyRegistered): |
86 | self.client.register(email='blah') |
87 | |
88 | - @patch(REQUEST, return_value=mock_response(201)) |
89 | - def test_register_success(self, mock_request): |
90 | + def test_register_success(self): |
91 | + self.mock_request.return_value = mock_response(201) |
92 | self.client.register(email='blah') |
93 | - args = mock_request.call_args[0] |
94 | + args = self.mock_request.call_args[0] |
95 | self.assertEqual(args, ('POST', 'http://foo.com/accounts')) |
96 | |
97 | def test_invalid_response_400(self): |
98 | @@ -153,160 +181,275 @@ |
99 | |
100 | class LoginV2ClientApiTestCase(V2ClientApiTestCase): |
101 | |
102 | - @patch(REQUEST, return_value=mock_response(400, code="INVALID_DATA")) |
103 | - def test_login_invalid_data(self, mock_request): |
104 | + def test_login_invalid_data(self): |
105 | + self.mock_request.return_value = mock_response( |
106 | + 400, code="INVALID_DATA") |
107 | with self.assertRaises(errors.InvalidData): |
108 | self.client.login(email='blah') |
109 | |
110 | - @patch(REQUEST, return_value=mock_response(401, code="ACCOUNT_SUSPENDED")) |
111 | - def test_login_account_suspended(self, mock_request): |
112 | + def test_login_account_suspended(self): |
113 | + self.mock_request.return_value = mock_response( |
114 | + 401, code="ACCOUNT_SUSPENDED") |
115 | with self.assertRaises(errors.AccountSuspended): |
116 | self.client.login(email='blah') |
117 | |
118 | - @patch(REQUEST, return_value=mock_response( |
119 | - 401, code="ACCOUNT_DEACTIVATED")) |
120 | - def test_login_account_deactivated(self, mock_request): |
121 | + def test_login_account_deactivated(self): |
122 | + self.mock_request.return_value = mock_response( |
123 | + 401, code="ACCOUNT_DEACTIVATED") |
124 | with self.assertRaises(errors.AccountDeactivated): |
125 | self.client.login(email='blah') |
126 | |
127 | - @patch(REQUEST, return_value=mock_response( |
128 | - 401, code="INVALID_CREDENTIALS")) |
129 | - def test_login_invalid_credentials(self, mock_request): |
130 | + def test_login_invalid_credentials(self): |
131 | + self.mock_request.return_value = mock_response( |
132 | + 401, code="INVALID_CREDENTIALS") |
133 | with self.assertRaises(errors.InvalidCredentials): |
134 | self.client.login(email='blah') |
135 | |
136 | - @patch(REQUEST, return_value=mock_response(401, code="TWOFACTOR_REQUIRED")) |
137 | - def test_login_twofactor_required(self, mock_request): |
138 | + def test_login_twofactor_required(self): |
139 | + self.mock_request.return_value = mock_response( |
140 | + 401, code="TWOFACTOR_REQUIRED") |
141 | with self.assertRaises(errors.TwoFactorRequired): |
142 | self.client.login(email='blah') |
143 | |
144 | - @patch(REQUEST, return_value=mock_response(403, code="TWOFACTOR_FAILURE")) |
145 | - def test_login_twofactor_failure(self, mock_request): |
146 | + def test_login_twofactor_failure(self): |
147 | + self.mock_request.return_value = mock_response( |
148 | + 403, code="TWOFACTOR_FAILURE") |
149 | with self.assertRaises(errors.TwoFactorFailure): |
150 | self.client.login(email='blah') |
151 | |
152 | + def test_login_account_locked(self): |
153 | + self.mock_request.return_value = mock_response( |
154 | + 403, code="ACCOUNT_LOCKED") |
155 | + with self.assertRaises(errors.AccountLocked): |
156 | + self.client.login(email='blah') |
157 | + |
158 | + def test_login_email_invalidated(self): |
159 | + self.mock_request.return_value = mock_response( |
160 | + 403, code="EMAIL_INVALIDATED") |
161 | + with self.assertRaises(errors.EmailInvalidated): |
162 | + self.client.login(email='blah') |
163 | + |
164 | |
165 | class PasswordResetV2ClientApiTestCase(V2ClientApiTestCase): |
166 | |
167 | - @patch(REQUEST, return_value=mock_response(201)) |
168 | - def test_request_password_reset(self, mock_request): |
169 | + def test_request_password_reset(self): |
170 | + self.mock_request.return_value = mock_response(201) |
171 | response = self.client.request_password_reset('foo@foo.com') |
172 | self.assertEqual(response.status_code, 201) |
173 | - self.assertEqual(mock_request.call_args, [ |
174 | + self.assertEqual(self.mock_request.call_args, [ |
175 | ('POST', 'http://foo.com/tokens/password'), |
176 | {'headers': {'Content-Type': 'application/json'}, |
177 | 'data': '{"token": null, "email": "foo@foo.com"}'}, |
178 | ]) |
179 | |
180 | - @patch(REQUEST, return_value=mock_response(400, code="INVALID_DATA")) |
181 | - def test_request_password_reset_without_email(self, mock_request): |
182 | + def test_request_password_reset_without_email(self): |
183 | + self.mock_request.return_value = mock_response( |
184 | + 400, code="INVALID_DATA") |
185 | with self.assertRaises(errors.InvalidData): |
186 | self.client.request_password_reset(None) |
187 | |
188 | - @patch(REQUEST, return_value=mock_response(400, code="INVALID_DATA")) |
189 | - def test_request_password_reset_with_empty_email(self, mock_request): |
190 | + def test_request_password_reset_with_empty_email(self): |
191 | + self.mock_request.return_value = mock_response( |
192 | + 400, code="INVALID_DATA") |
193 | with self.assertRaises(errors.InvalidData): |
194 | self.client.request_password_reset('') |
195 | |
196 | - @patch(REQUEST, return_value=mock_response(201)) |
197 | - def test_request_password_reset_with_token(self, mock_request): |
198 | + def test_request_password_reset_with_token(self): |
199 | + self.mock_request.return_value = mock_response(201) |
200 | response = self.client.request_password_reset('foo@foo.com', |
201 | 'token1234') |
202 | self.assertEqual(response.status_code, 201) |
203 | - self.assertEqual(mock_request.call_args, [ |
204 | + self.assertEqual(self.mock_request.call_args, [ |
205 | ('POST', 'http://foo.com/tokens/password'), |
206 | {'headers': {'Content-Type': 'application/json'}, |
207 | 'data': '{"token": "token1234", "email": "foo@foo.com"}'}, |
208 | ]) |
209 | |
210 | - @patch(REQUEST, return_value=mock_response(403, code="ACCOUNT_SUSPENDED")) |
211 | - def test_request_password_reset_for_suspended_account(self, mock_request): |
212 | + def test_request_password_reset_for_suspended_account(self): |
213 | + self.mock_request.return_value = mock_response( |
214 | + 403, code="ACCOUNT_SUSPENDED") |
215 | with self.assertRaises(errors.AccountSuspended): |
216 | self.client.request_password_reset('foo@foo.com') |
217 | |
218 | - @patch(REQUEST, return_value=mock_response( |
219 | - 403, code="ACCOUNT_DEACTIVATED")) |
220 | - def test_request_password_reset_for_deactivated_account(self, |
221 | - mock_request): |
222 | + def test_request_password_reset_for_deactivated_account(self): |
223 | + self.mock_request.return_value = mock_response( |
224 | + 403, code="ACCOUNT_DEACTIVATED") |
225 | with self.assertRaises(errors.AccountDeactivated): |
226 | self.client.request_password_reset('foo@foo.com') |
227 | |
228 | - @patch(REQUEST, return_value=mock_response( |
229 | - 403, code="RESOURCE_NOT_FOUND")) |
230 | - def test_request_password_reset_with_invalid_email(self, mock_request): |
231 | + def test_request_password_reset_with_invalid_email(self): |
232 | + self.mock_request.return_value = mock_response( |
233 | + 403, code="RESOURCE_NOT_FOUND") |
234 | with self.assertRaises(errors.ResourceNotFound): |
235 | self.client.request_password_reset('foo@foo.com') |
236 | |
237 | - @patch(REQUEST, return_value=mock_response( |
238 | - 403, code="CAN_NOT_RESET_PASSWORD")) |
239 | - def test_request_password_reset_not_allowed(self, mock_request): |
240 | + def test_request_password_reset_not_allowed(self): |
241 | + self.mock_request.return_value = mock_response( |
242 | + 403, code="CAN_NOT_RESET_PASSWORD") |
243 | with self.assertRaises(errors.CanNotResetPassword): |
244 | self.client.request_password_reset('foo@foo.com') |
245 | |
246 | - @patch(REQUEST, return_value=mock_response(403, code="EMAIL_INVALIDATED")) |
247 | - def test_request_password_reset_with_invalidated_email(self, |
248 | - mock_request): |
249 | + def test_request_password_reset_with_invalidated_email(self): |
250 | + self.mock_request.return_value = mock_response( |
251 | + 403, code="EMAIL_INVALIDATED") |
252 | with self.assertRaises(errors.EmailInvalidated): |
253 | self.client.request_password_reset('foo@foo.com') |
254 | |
255 | - @patch(REQUEST, return_value=mock_response(403, code="TOO_MANY_TOKENS")) |
256 | - def test_request_password_reset_with_too_many_tokens(self, mock_request): |
257 | + def test_request_password_reset_with_too_many_tokens(self): |
258 | + self.mock_request.return_value = mock_response( |
259 | + 403, code="TOO_MANY_TOKENS") |
260 | with self.assertRaises(errors.TooManyTokens): |
261 | self.client.request_password_reset('foo@foo.com') |
262 | |
263 | |
264 | class AccountDetailsV2ClientApiTestCase(V2ClientApiTestCase): |
265 | |
266 | - @patch(REQUEST, return_value=mock_response(200)) |
267 | - def test_account_details(self, mock_request): |
268 | - token = dict( |
269 | - consumer_key='consumer_key', |
270 | - consumer_secret='consumer_secret', |
271 | - token_key='token_key', |
272 | - token_secret='token_secret', |
273 | - ) |
274 | - with patch('ssoclient.v2.client.OAuth1') as mock_oauth: |
275 | - response = self.client.account_details('some_openid', token) |
276 | - |
277 | - mock_oauth.assert_called_once_with( |
278 | - 'consumer_key', 'consumer_secret', 'token_key', 'token_secret' |
279 | - ) |
280 | - self.assertTrue(all(isinstance(val, unicode) for |
281 | - val in mock_oauth.call_args[0])) |
282 | - |
283 | - oauth1 = mock_oauth.return_value |
284 | - mock_request.assert_called_once_with( |
285 | + def test_account_details(self): |
286 | + self.mock_request.return_value = mock_response(200) |
287 | + response = self.client.account_details('some_openid', self.credentials) |
288 | + self.assert_unicode_credentials(self.credentials) |
289 | + |
290 | + oauth1 = self.mock_oauth.return_value |
291 | + self.mock_request.assert_called_once_with( |
292 | 'GET', 'http://foo.com/accounts/some_openid', auth=oauth1, |
293 | headers={}, allow_redirects=True, |
294 | ) |
295 | |
296 | # The response is mocked - so this test just confirms that the |
297 | # account_details method "does the right thing" and returns our mock |
298 | - # repsonse |
299 | + # response |
300 | self.assertEqual(response.status_code, 200) |
301 | |
302 | - @patch(REQUEST, return_value=mock_response(200)) |
303 | - def test_account_details_anonymous(self, mock_request): |
304 | + def test_account_details_anonymous(self): |
305 | + self.mock_request.return_value = mock_response(200) |
306 | response = self.client.account_details('some_openid') |
307 | - mock_request.assert_called_once_with( |
308 | + self.mock_request.assert_called_once_with( |
309 | 'GET', 'http://foo.com/accounts/some_openid', auth=None, |
310 | headers={}, allow_redirects=True, |
311 | ) |
312 | self.assertEqual(response.status_code, 200) |
313 | |
314 | |
315 | +class EmailsV2ClientApiTestCase(V2ClientApiTestCase): |
316 | + |
317 | + def test_details(self): |
318 | + self.mock_request.return_value = mock_response(200) |
319 | + |
320 | + response = self.client.email_details('email', self.credentials) |
321 | + self.assert_unicode_credentials(self.credentials) |
322 | + |
323 | + oauth1 = self.mock_oauth.return_value |
324 | + self.mock_request.assert_called_once_with( |
325 | + 'GET', 'http://foo.com/emails/email', auth=oauth1, |
326 | + headers={}, allow_redirects=True, |
327 | + ) |
328 | + |
329 | + self.assertEqual(response.status_code, 200) |
330 | + |
331 | + def test_details_invalid_credentials(self): |
332 | + self.mock_request.return_value = mock_response( |
333 | + 401, code="INVALID_CREDENTIALS") |
334 | + with self.assertRaises(errors.InvalidCredentials): |
335 | + self.client.email_details('blah', {}) |
336 | + |
337 | + def test_details_not_found(self): |
338 | + self.mock_request.return_value = mock_response( |
339 | + 404, code="RESOURCE_NOT_FOUND") |
340 | + with self.assertRaises(errors.ResourceNotFound): |
341 | + self.client.email_details('blah', {}) |
342 | + |
343 | + def test_delete(self): |
344 | + self.mock_request.return_value = mock_response(204) |
345 | + |
346 | + response = self.client.email_delete('email', self.credentials) |
347 | + self.assert_unicode_credentials(self.credentials) |
348 | + |
349 | + oauth1 = self.mock_oauth.return_value |
350 | + self.mock_request.assert_called_once_with( |
351 | + 'DELETE', 'http://foo.com/emails/email', auth=oauth1, |
352 | + headers={}, |
353 | + ) |
354 | + |
355 | + self.assertEqual(response.status_code, 204) |
356 | + |
357 | + def test_delete_invalid_credentials(self): |
358 | + self.mock_request.return_value = mock_response( |
359 | + 401, code="INVALID_CREDENTIALS") |
360 | + with self.assertRaises(errors.InvalidCredentials): |
361 | + self.client.email_delete('blah', {}) |
362 | + |
363 | + def test_delete_not_found(self): |
364 | + self.mock_request.return_value = mock_response( |
365 | + 404, code="RESOURCE_NOT_FOUND") |
366 | + with self.assertRaises(errors.ResourceNotFound): |
367 | + self.client.email_delete('blah', {}) |
368 | + |
369 | + |
370 | +class TokensV2ClientApiTestCase(V2ClientApiTestCase): |
371 | + |
372 | + def test_details(self): |
373 | + self.mock_request.return_value = mock_response(200) |
374 | + |
375 | + response = self.client.token_details('token_key', self.credentials) |
376 | + self.assert_unicode_credentials(self.credentials) |
377 | + |
378 | + oauth1 = self.mock_oauth.return_value |
379 | + self.mock_request.assert_called_once_with( |
380 | + 'GET', 'http://foo.com/tokens/oauth/token_key', auth=oauth1, |
381 | + headers={}, allow_redirects=True, |
382 | + ) |
383 | + |
384 | + self.assertEqual(response.status_code, 200) |
385 | + |
386 | + def test_details_invalid_credentials(self): |
387 | + self.mock_request.return_value = mock_response( |
388 | + 401, code="INVALID_CREDENTIALS") |
389 | + with self.assertRaises(errors.InvalidCredentials): |
390 | + self.client.token_details('blah', {}) |
391 | + |
392 | + def test_details_not_found(self): |
393 | + self.mock_request.return_value = mock_response( |
394 | + 404, code="RESOURCE_NOT_FOUND") |
395 | + with self.assertRaises(errors.ResourceNotFound): |
396 | + self.client.token_details('blah', {}) |
397 | + |
398 | + def test_delete(self): |
399 | + self.mock_request.return_value = mock_response(204) |
400 | + |
401 | + response = self.client.token_delete('token_key', self.credentials) |
402 | + self.assert_unicode_credentials(self.credentials) |
403 | + |
404 | + oauth1 = self.mock_oauth.return_value |
405 | + self.mock_request.assert_called_once_with( |
406 | + 'DELETE', 'http://foo.com/tokens/oauth/token_key', auth=oauth1, |
407 | + headers={}, |
408 | + ) |
409 | + |
410 | + self.assertEqual(response.status_code, 204) |
411 | + |
412 | + def test_delete_invalid_credentials(self): |
413 | + self.mock_request.return_value = mock_response( |
414 | + 401, code="INVALID_CREDENTIALS") |
415 | + with self.assertRaises(errors.InvalidCredentials): |
416 | + self.client.token_delete('blah', {}) |
417 | + |
418 | + def test_delete_not_found(self): |
419 | + self.mock_request.return_value = mock_response( |
420 | + 404, code="RESOURCE_NOT_FOUND") |
421 | + with self.assertRaises(errors.ResourceNotFound): |
422 | + self.client.token_delete('blah', {}) |
423 | + |
424 | + |
425 | class ValidateRequestV2ClientApiTestCase(V2ClientApiTestCase): |
426 | |
427 | - @patch(REQUEST) |
428 | - def test_valid_request(self, mock_request): |
429 | - mock_request.return_value = mock_response(200, is_valid=True) |
430 | + def test_valid_request(self): |
431 | + self.mock_request.return_value = mock_response(200, is_valid=True) |
432 | result = self.client.validate_request( |
433 | http_url='foo', http_method='GET', authorization='123456789') |
434 | self.assertEqual(result.json(), {'is_valid': True}) |
435 | |
436 | - @patch(REQUEST) |
437 | - def test_invalid_request(self, mock_request): |
438 | - mock_request.return_value = mock_response(200, is_valid=False) |
439 | + def test_invalid_request(self): |
440 | + self.mock_request.return_value = mock_response(200, is_valid=False) |
441 | result = self.client.validate_request( |
442 | http_url='foo', http_method='GET', authorization='123456789') |
443 | self.assertEqual(result.json(), {'is_valid': False}) |
444 | |
445 | === modified file 'ssoclient/v2/client.py' |
446 | --- ssoclient/v2/client.py 2013-05-22 16:52:20 +0000 |
447 | +++ ssoclient/v2/client.py 2013-08-19 16:45:04 +0000 |
448 | @@ -13,6 +13,25 @@ |
449 | def __init__(self, endpoint): |
450 | self.session = ApiSession(endpoint) |
451 | |
452 | + def _unicode_credentials(self, credentials): |
453 | + # if openid and credentials come directly from a call to client.login |
454 | + # then whether they are unicode or byte-strings depends on which |
455 | + # json library is in use. |
456 | + # oauthlib requires them to be unicode - so we coerce to be sure. |
457 | + if credentials is not None: |
458 | + consumer_key = unicode(credentials.get('consumer_key', '')) |
459 | + consumer_secret = unicode(credentials.get('consumer_secret', '')) |
460 | + token_key = unicode(credentials.get('token_key', '')) |
461 | + token_secret = unicode(credentials.get('token_secret', '')) |
462 | + oauth = OAuth1( |
463 | + consumer_key, |
464 | + consumer_secret, |
465 | + token_key, token_secret, |
466 | + ) |
467 | + else: |
468 | + oauth = None |
469 | + return oauth |
470 | + |
471 | def _merge(self, data, extra): |
472 | """Allows data to passed to functions by keyword or dict""" |
473 | if data: |
474 | @@ -30,7 +49,9 @@ |
475 | return self.session.post('/accounts', data=self._merge(data, kwargs)) |
476 | |
477 | @api_exception(errors.AccountDeactivated) |
478 | + @api_exception(errors.AccountLocked) |
479 | @api_exception(errors.AccountSuspended) |
480 | + @api_exception(errors.EmailInvalidated) |
481 | @api_exception(errors.InvalidCredentials) |
482 | @api_exception(errors.InvalidData) |
483 | @api_exception(errors.TwoFactorFailure) |
484 | @@ -41,25 +62,34 @@ |
485 | @api_exception(errors.InvalidCredentials) |
486 | @api_exception(errors.ResourceNotFound) |
487 | def account_details(self, openid, token=None): |
488 | - # if openid and token come directly from a call to client.login |
489 | - # then whether they are unicode or byte-strings depends on which |
490 | - # json library is in use. |
491 | - # oauthlib requires them to be unicode - so we coerce to be sure. |
492 | openid = unicode(openid) |
493 | - if token is not None: |
494 | - consumer_key = unicode(token['consumer_key']) |
495 | - consumer_secret = unicode(token['consumer_secret']) |
496 | - token_key = unicode(token['token_key']) |
497 | - token_secret = unicode(token['token_secret']) |
498 | - oauth = OAuth1( |
499 | - consumer_key, |
500 | - consumer_secret, |
501 | - token_key, token_secret, |
502 | - ) |
503 | - else: |
504 | - oauth = None |
505 | + oauth = self._unicode_credentials(token) |
506 | return self.session.get('/accounts/%s' % openid, auth=oauth) |
507 | |
508 | + @api_exception(errors.InvalidCredentials) |
509 | + @api_exception(errors.ResourceNotFound) |
510 | + def email_delete(self, email, credentials): |
511 | + oauth = self._unicode_credentials(credentials) |
512 | + return self.session.delete('/emails/%s' % email, auth=oauth) |
513 | + |
514 | + @api_exception(errors.InvalidCredentials) |
515 | + @api_exception(errors.ResourceNotFound) |
516 | + def email_details(self, email, credentials): |
517 | + oauth = self._unicode_credentials(credentials) |
518 | + return self.session.get('/emails/%s' % email, auth=oauth) |
519 | + |
520 | + @api_exception(errors.InvalidCredentials) |
521 | + @api_exception(errors.ResourceNotFound) |
522 | + def token_delete(self, token_key, credentials): |
523 | + oauth = self._unicode_credentials(credentials) |
524 | + return self.session.delete('/tokens/oauth/%s' % token_key, auth=oauth) |
525 | + |
526 | + @api_exception(errors.InvalidCredentials) |
527 | + @api_exception(errors.ResourceNotFound) |
528 | + def token_details(self, token_key, credentials): |
529 | + oauth = self._unicode_credentials(credentials) |
530 | + return self.session.get('/tokens/oauth/%s' % token_key, auth=oauth) |
531 | + |
532 | def validate_request(self, data=None, **kwargs): |
533 | return self.session.post('/requests/validate', |
534 | data=self._merge(data, kwargs)) |
535 | |
536 | === modified file 'ssoclient/v2/errors.py' |
537 | --- ssoclient/v2/errors.py 2013-05-24 12:27:05 +0000 |
538 | +++ ssoclient/v2/errors.py 2013-08-19 16:45:04 +0000 |
539 | @@ -28,6 +28,10 @@ |
540 | error_code = "ACCOUNT_DEACTIVATED" |
541 | |
542 | |
543 | +class AccountLocked(ApiException): |
544 | + error_code = "ACCOUNT_LOCKED" |
545 | + |
546 | + |
547 | class EmailInvalidated(ApiException): |
548 | error_code = "EMAIL_INVALIDATED" |
549 |