test_scope_handling.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. """Ensure scope is preserved across authorization.
  2. Fairly trivial in all grants except the Authorization Code Grant where scope
  3. need to be persisted temporarily in an authorization code.
  4. """
  5. import json
  6. from unittest import mock
  7. from oauthlib.oauth2 import (
  8. BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
  9. RequestValidator, Server, WebApplicationServer,
  10. )
  11. from tests.unittest import TestCase
  12. from .test_utils import get_fragment_credentials, get_query_credentials
  13. class TestScopeHandling(TestCase):
  14. DEFAULT_REDIRECT_URI = 'http://i.b./path'
  15. def set_scopes(self, scopes):
  16. def set_request_scopes(client_id, code, client, request):
  17. request.scopes = scopes
  18. return True
  19. return set_request_scopes
  20. def set_user(self, request):
  21. request.user = 'foo'
  22. request.client_id = 'bar'
  23. request.client = mock.MagicMock()
  24. request.client.client_id = 'mocked'
  25. return True
  26. def set_client(self, request):
  27. request.client = mock.MagicMock()
  28. request.client.client_id = 'mocked'
  29. return True
  30. def setUp(self):
  31. self.validator = mock.MagicMock(spec=RequestValidator)
  32. self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI
  33. self.validator.get_code_challenge.return_value = None
  34. self.validator.authenticate_client.side_effect = self.set_client
  35. self.server = Server(self.validator)
  36. self.web = WebApplicationServer(self.validator)
  37. self.mobile = MobileApplicationServer(self.validator)
  38. self.legacy = LegacyApplicationServer(self.validator)
  39. self.backend = BackendApplicationServer(self.validator)
  40. def test_scope_extraction(self):
  41. scopes = (
  42. ('images', ['images']),
  43. ('images+videos', ['images', 'videos']),
  44. ('images+videos+openid', ['images', 'videos', 'openid']),
  45. ('http%3A%2f%2fa.b%2fvideos', ['http://a.b/videos']),
  46. ('http%3A%2f%2fa.b%2fvideos+pics', ['http://a.b/videos', 'pics']),
  47. ('pics+http%3A%2f%2fa.b%2fvideos', ['pics', 'http://a.b/videos']),
  48. ('http%3A%2f%2fa.b%2fvideos+https%3A%2f%2fc.d%2Fsecret', ['http://a.b/videos', 'https://c.d/secret']),
  49. )
  50. uri = 'http://example.com/path?client_id=abc&scope=%s&response_type=%s'
  51. for scope, correct_scopes in scopes:
  52. scopes, _ = self.web.validate_authorization_request(
  53. uri % (scope, 'code'))
  54. self.assertCountEqual(scopes, correct_scopes)
  55. scopes, _ = self.mobile.validate_authorization_request(
  56. uri % (scope, 'token'))
  57. self.assertCountEqual(scopes, correct_scopes)
  58. scopes, _ = self.server.validate_authorization_request(
  59. uri % (scope, 'code'))
  60. self.assertCountEqual(scopes, correct_scopes)
  61. def test_scope_preservation(self):
  62. scope = 'pics+http%3A%2f%2fa.b%2fvideos'
  63. decoded_scope = 'pics http://a.b/videos'
  64. auth_uri = 'http://example.com/path?client_id=abc&response_type='
  65. token_uri = 'http://example.com/path'
  66. # authorization grant
  67. for backend_server_type in ['web', 'server']:
  68. h, _, s = getattr(self, backend_server_type).create_authorization_response(
  69. auth_uri + 'code', scopes=decoded_scope.split(' '))
  70. self.validator.validate_code.side_effect = self.set_scopes(decoded_scope.split(' '))
  71. self.assertEqual(s, 302)
  72. self.assertIn('Location', h)
  73. code = get_query_credentials(h['Location'])['code'][0]
  74. _, body, _ = getattr(self, backend_server_type).create_token_response(token_uri,
  75. body='client_id=me&redirect_uri=http://back.to/me&grant_type=authorization_code&code=%s' % code)
  76. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  77. # implicit grant
  78. for backend_server_type in ['mobile', 'server']:
  79. h, _, s = getattr(self, backend_server_type).create_authorization_response(
  80. auth_uri + 'token', scopes=decoded_scope.split(' '))
  81. self.assertEqual(s, 302)
  82. self.assertIn('Location', h)
  83. self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope)
  84. # resource owner password credentials grant
  85. for backend_server_type in ['legacy', 'server']:
  86. body = 'grant_type=password&username=abc&password=secret&scope=%s'
  87. _, body, _ = getattr(self, backend_server_type).create_token_response(token_uri,
  88. body=body % scope)
  89. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  90. # client credentials grant
  91. for backend_server_type in ['backend', 'server']:
  92. body = 'grant_type=client_credentials&scope=%s'
  93. self.validator.authenticate_client.side_effect = self.set_user
  94. _, body, _ = getattr(self, backend_server_type).create_token_response(token_uri,
  95. body=body % scope)
  96. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  97. def test_scope_changed(self):
  98. scope = 'pics+http%3A%2f%2fa.b%2fvideos'
  99. scopes = ['images', 'http://a.b/videos']
  100. decoded_scope = 'images http://a.b/videos'
  101. auth_uri = 'http://example.com/path?client_id=abc&response_type='
  102. token_uri = 'http://example.com/path'
  103. # authorization grant
  104. h, _, s = self.web.create_authorization_response(
  105. auth_uri + 'code', scopes=scopes)
  106. self.assertEqual(s, 302)
  107. self.assertIn('Location', h)
  108. code = get_query_credentials(h['Location'])['code'][0]
  109. self.validator.validate_code.side_effect = self.set_scopes(scopes)
  110. _, body, _ = self.web.create_token_response(token_uri,
  111. body='grant_type=authorization_code&code=%s' % code)
  112. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  113. # implicit grant
  114. self.validator.validate_scopes.side_effect = self.set_scopes(scopes)
  115. h, _, s = self.mobile.create_authorization_response(
  116. auth_uri + 'token', scopes=scopes)
  117. self.assertEqual(s, 302)
  118. self.assertIn('Location', h)
  119. self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope)
  120. # resource owner password credentials grant
  121. self.validator.validate_scopes.side_effect = self.set_scopes(scopes)
  122. body = 'grant_type=password&username=abc&password=secret&scope=%s'
  123. _, body, _ = self.legacy.create_token_response(token_uri,
  124. body=body % scope)
  125. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  126. # client credentials grant
  127. self.validator.validate_scopes.side_effect = self.set_scopes(scopes)
  128. self.validator.authenticate_client.side_effect = self.set_user
  129. body = 'grant_type=client_credentials&scope=%s'
  130. _, body, _ = self.backend.create_token_response(token_uri,
  131. body=body % scope)
  132. self.assertEqual(json.loads(body)['scope'], decoded_scope)
  133. def test_invalid_scope(self):
  134. scope = 'pics+http%3A%2f%2fa.b%2fvideos'
  135. auth_uri = 'http://example.com/path?client_id=abc&response_type='
  136. token_uri = 'http://example.com/path'
  137. self.validator.validate_scopes.return_value = False
  138. # authorization grant
  139. h, _, s = self.web.create_authorization_response(
  140. auth_uri + 'code', scopes=['invalid'])
  141. self.assertEqual(s, 302)
  142. self.assertIn('Location', h)
  143. error = get_query_credentials(h['Location'])['error'][0]
  144. self.assertEqual(error, 'invalid_scope')
  145. # implicit grant
  146. h, _, s = self.mobile.create_authorization_response(
  147. auth_uri + 'token', scopes=['invalid'])
  148. self.assertEqual(s, 302)
  149. self.assertIn('Location', h)
  150. error = get_fragment_credentials(h['Location'])['error'][0]
  151. self.assertEqual(error, 'invalid_scope')
  152. # resource owner password credentials grant
  153. body = 'grant_type=password&username=abc&password=secret&scope=%s'
  154. _, body, _ = self.legacy.create_token_response(token_uri,
  155. body=body % scope)
  156. self.assertEqual(json.loads(body)['error'], 'invalid_scope')
  157. # client credentials grant
  158. self.validator.authenticate_client.side_effect = self.set_user
  159. body = 'grant_type=client_credentials&scope=%s'
  160. _, body, _ = self.backend.create_token_response(token_uri,
  161. body=body % scope)
  162. self.assertEqual(json.loads(body)['error'], 'invalid_scope')