test_server.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. # -*- coding: utf-8 -*-
  2. import json
  3. from unittest import mock
  4. from oauthlib import common
  5. from oauthlib.oauth2.rfc6749 import errors, tokens
  6. from oauthlib.oauth2.rfc6749.endpoints import Server
  7. from oauthlib.oauth2.rfc6749.endpoints.authorization import (
  8. AuthorizationEndpoint,
  9. )
  10. from oauthlib.oauth2.rfc6749.endpoints.resource import ResourceEndpoint
  11. from oauthlib.oauth2.rfc6749.endpoints.token import TokenEndpoint
  12. from oauthlib.oauth2.rfc6749.grant_types import (
  13. AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
  14. ResourceOwnerPasswordCredentialsGrant,
  15. )
  16. from tests.unittest import TestCase
  17. class AuthorizationEndpointTest(TestCase):
  18. def setUp(self):
  19. self.mock_validator = mock.MagicMock()
  20. self.mock_validator.get_code_challenge.return_value = None
  21. self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
  22. auth_code = AuthorizationCodeGrant(
  23. request_validator=self.mock_validator)
  24. auth_code.save_authorization_code = mock.MagicMock()
  25. implicit = ImplicitGrant(
  26. request_validator=self.mock_validator)
  27. implicit.save_token = mock.MagicMock()
  28. response_types = {
  29. 'code': auth_code,
  30. 'token': implicit,
  31. 'none': auth_code
  32. }
  33. self.expires_in = 1800
  34. token = tokens.BearerToken(
  35. self.mock_validator,
  36. expires_in=self.expires_in
  37. )
  38. self.endpoint = AuthorizationEndpoint(
  39. default_response_type='code',
  40. default_token_type=token,
  41. response_types=response_types
  42. )
  43. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  44. def test_authorization_grant(self):
  45. uri = 'http://i.b/l?response_type=code&client_id=me&scope=all+of+them&state=xyz'
  46. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  47. headers, body, status_code = self.endpoint.create_authorization_response(
  48. uri, scopes=['all', 'of', 'them'])
  49. self.assertIn('Location', headers)
  50. self.assertURLEqual(headers['Location'], 'http://back.to/me?code=abc&state=xyz')
  51. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  52. def test_implicit_grant(self):
  53. uri = 'http://i.b/l?response_type=token&client_id=me&scope=all+of+them&state=xyz'
  54. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  55. headers, body, status_code = self.endpoint.create_authorization_response(
  56. uri, scopes=['all', 'of', 'them'])
  57. self.assertIn('Location', headers)
  58. self.assertURLEqual(headers['Location'], 'http://back.to/me#access_token=abc&expires_in=' + str(self.expires_in) + '&token_type=Bearer&state=xyz&scope=all+of+them', parse_fragment=True)
  59. def test_none_grant(self):
  60. uri = 'http://i.b/l?response_type=none&client_id=me&scope=all+of+them&state=xyz'
  61. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  62. headers, body, status_code = self.endpoint.create_authorization_response(
  63. uri, scopes=['all', 'of', 'them'])
  64. self.assertIn('Location', headers)
  65. self.assertURLEqual(headers['Location'], 'http://back.to/me?state=xyz', parse_fragment=True)
  66. self.assertIsNone(body)
  67. self.assertEqual(status_code, 302)
  68. # and without the state parameter
  69. uri = 'http://i.b/l?response_type=none&client_id=me&scope=all+of+them'
  70. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  71. headers, body, status_code = self.endpoint.create_authorization_response(
  72. uri, scopes=['all', 'of', 'them'])
  73. self.assertIn('Location', headers)
  74. self.assertURLEqual(headers['Location'], 'http://back.to/me', parse_fragment=True)
  75. self.assertIsNone(body)
  76. self.assertEqual(status_code, 302)
  77. def test_missing_type(self):
  78. uri = 'http://i.b/l?client_id=me&scope=all+of+them'
  79. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  80. self.mock_validator.validate_request = mock.MagicMock(
  81. side_effect=errors.InvalidRequestError())
  82. headers, body, status_code = self.endpoint.create_authorization_response(
  83. uri, scopes=['all', 'of', 'them'])
  84. self.assertIn('Location', headers)
  85. self.assertURLEqual(headers['Location'], 'http://back.to/me?error=invalid_request&error_description=Missing+response_type+parameter.')
  86. def test_invalid_type(self):
  87. uri = 'http://i.b/l?response_type=invalid&client_id=me&scope=all+of+them'
  88. uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme'
  89. self.mock_validator.validate_request = mock.MagicMock(
  90. side_effect=errors.UnsupportedResponseTypeError())
  91. headers, body, status_code = self.endpoint.create_authorization_response(
  92. uri, scopes=['all', 'of', 'them'])
  93. self.assertIn('Location', headers)
  94. self.assertURLEqual(headers['Location'], 'http://back.to/me?error=unsupported_response_type')
  95. class TokenEndpointTest(TestCase):
  96. def setUp(self):
  97. def set_user(request):
  98. request.user = mock.MagicMock()
  99. request.client = mock.MagicMock()
  100. request.client.client_id = 'mocked_client_id'
  101. return True
  102. self.mock_validator = mock.MagicMock()
  103. self.mock_validator.authenticate_client.side_effect = set_user
  104. self.mock_validator.get_code_challenge.return_value = None
  105. self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
  106. auth_code = AuthorizationCodeGrant(
  107. request_validator=self.mock_validator)
  108. password = ResourceOwnerPasswordCredentialsGrant(
  109. request_validator=self.mock_validator)
  110. client = ClientCredentialsGrant(
  111. request_validator=self.mock_validator)
  112. supported_types = {
  113. 'authorization_code': auth_code,
  114. 'password': password,
  115. 'client_credentials': client,
  116. }
  117. self.expires_in = 1800
  118. token = tokens.BearerToken(
  119. self.mock_validator,
  120. expires_in=self.expires_in
  121. )
  122. self.endpoint = TokenEndpoint(
  123. 'authorization_code',
  124. default_token_type=token,
  125. grant_types=supported_types
  126. )
  127. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  128. def test_authorization_grant(self):
  129. body = 'grant_type=authorization_code&code=abc&scope=all+of+them'
  130. headers, body, status_code = self.endpoint.create_token_response(
  131. '', body=body)
  132. token = {
  133. 'token_type': 'Bearer',
  134. 'expires_in': self.expires_in,
  135. 'access_token': 'abc',
  136. 'refresh_token': 'abc',
  137. 'scope': 'all of them'
  138. }
  139. self.assertEqual(json.loads(body), token)
  140. body = 'grant_type=authorization_code&code=abc'
  141. headers, body, status_code = self.endpoint.create_token_response(
  142. '', body=body)
  143. token = {
  144. 'token_type': 'Bearer',
  145. 'expires_in': self.expires_in,
  146. 'access_token': 'abc',
  147. 'refresh_token': 'abc'
  148. }
  149. self.assertEqual(json.loads(body), token)
  150. # try with additional custom variables
  151. body = 'grant_type=authorization_code&code=abc&state=foobar'
  152. headers, body, status_code = self.endpoint.create_token_response(
  153. '', body=body)
  154. self.assertEqual(json.loads(body), token)
  155. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  156. def test_password_grant(self):
  157. body = 'grant_type=password&username=a&password=hello&scope=all+of+them'
  158. headers, body, status_code = self.endpoint.create_token_response(
  159. '', body=body)
  160. token = {
  161. 'token_type': 'Bearer',
  162. 'expires_in': self.expires_in,
  163. 'access_token': 'abc',
  164. 'refresh_token': 'abc',
  165. 'scope': 'all of them',
  166. }
  167. self.assertEqual(json.loads(body), token)
  168. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  169. def test_client_grant(self):
  170. body = 'grant_type=client_credentials&scope=all+of+them'
  171. headers, body, status_code = self.endpoint.create_token_response(
  172. '', body=body)
  173. token = {
  174. 'token_type': 'Bearer',
  175. 'expires_in': self.expires_in,
  176. 'access_token': 'abc',
  177. 'scope': 'all of them',
  178. }
  179. self.assertEqual(json.loads(body), token)
  180. def test_missing_type(self):
  181. _, body, _ = self.endpoint.create_token_response('', body='')
  182. token = {'error': 'unsupported_grant_type'}
  183. self.assertEqual(json.loads(body), token)
  184. def test_invalid_type(self):
  185. body = 'grant_type=invalid'
  186. _, body, _ = self.endpoint.create_token_response('', body=body)
  187. token = {'error': 'unsupported_grant_type'}
  188. self.assertEqual(json.loads(body), token)
  189. class SignedTokenEndpointTest(TestCase):
  190. def setUp(self):
  191. self.expires_in = 1800
  192. def set_user(request):
  193. request.user = mock.MagicMock()
  194. request.client = mock.MagicMock()
  195. request.client.client_id = 'mocked_client_id'
  196. return True
  197. self.mock_validator = mock.MagicMock()
  198. self.mock_validator.get_code_challenge.return_value = None
  199. self.mock_validator.authenticate_client.side_effect = set_user
  200. self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
  201. self.private_pem = """
  202. -----BEGIN RSA PRIVATE KEY-----
  203. MIIEpAIBAAKCAQEA6TtDhWGwzEOWZP6m/zHoZnAPLABfetvoMPmxPGjFjtDuMRPv
  204. EvI1sbixZBjBtdnc5rTtHUUQ25Am3JzwPRGo5laMGbj1pPyCPxlVi9LK82HQNX0B
  205. YK7tZtVfDHElQA7F4v3j9d3rad4O9/n+lyGIQ0tT7yQcBm2A8FEaP0bZYCLMjwMN
  206. WfaVLE8eXHyv+MfpNNLI9wttLxygKYM48I3NwsFuJgOa/KuodXaAmf8pJnx8t1Wn
  207. nxvaYXFiUn/TxmhM/qhemPa6+0nqq+aWV5eT7xn4K/ghLgNs09v6Yge0pmPl9Oz+
  208. +bjJ+aKRnAmwCOY8/5U5EilAiUOeBoO9+8OXtwIDAQABAoIBAGFTTbXXMkPK4HN8
  209. oItVdDlrAanG7hECuz3UtFUVE3upS/xG6TjqweVLwRqYCh2ssDXFwjy4mXRGDzF4
  210. e/e/6s9Txlrlh/w1MtTJ6ZzTdcViR9RKOczysjZ7S5KRlI3KnGFAuWPcG2SuOWjZ
  211. dZfzcj1Crd/ZHajBAVFHRsCo/ATVNKbTRprFfb27xKpQ2BwH/GG781sLE3ZVNIhs
  212. aRRaED4622kI1E/WXws2qQMqbFKzo0m1tPbLb3Z89WgZJ/tRQwuDype1Vfm7k6oX
  213. xfbp3948qSe/yWKRlMoPkleji/WxPkSIalzWSAi9ziN/0Uzhe65FURgrfHL3XR1A
  214. B8UR+aECgYEA7NPQZV4cAikk02Hv65JgISofqV49P8MbLXk8sdnI1n7Mj10TgzU3
  215. lyQGDEX4hqvT0bTXe4KAOxQZx9wumu05ejfzhdtSsEm6ptGHyCdmYDQeV0C/pxDX
  216. JNCK8XgMku2370XG0AnyBCT7NGlgtDcNCQufcesF2gEuoKiXg6Zjo7sCgYEA/Bzs
  217. 9fWGZZnSsMSBSW2OYbFuhF3Fne0HcxXQHipl0Rujc/9g0nccwqKGizn4fGOE7a8F
  218. usQgJoeGcinL7E9OEP/uQ9VX1C9RNVjIxP1O5/Guw1zjxQQYetOvbPhN2QhD1Ye7
  219. 0TRKrW1BapcjwLpFQlVg1ZeTPOi5lv24W/wX9jUCgYEAkrMSX/hPuTbrTNVZ3L6r
  220. NV/2hN+PaTPeXei/pBuXwOaCqDurnpcUfFcgN/IP5LwDVd+Dq0pHTFFDNv45EFbq
  221. R77o5n3ZVsIVEMiyJ1XgoK8oLDw7e61+15smtjT69Piz+09pu+ytMcwGn4y3Dmsb
  222. dALzHYnL8iLRU0ubrz0ec4kCgYAJiVKRTzNBPptQom49h85d9ac3jJCAE8o3WTjh
  223. Gzt0uHXrWlqgO280EY/DTnMOyXjqwLcXxHlu26uDP/99tdY/IF8z46sJ1KxetzgI
  224. 84f7kBHLRAU9m5UNeFpnZdEUB5MBTbwWAsNcYgiabpMkpCcghjg+fBhOsoLqqjhC
  225. CnwhjQKBgQDkv0QTdyBU84TE8J0XY3eLQwXbrvG2yD5A2ntN3PyxGEneX5WTJGMZ
  226. xJxwaFYQiDS3b9E7b8Q5dg8qa5Y1+epdhx3cuQAWPm+AoHKshDfbRve4txBDQAqh
  227. c6MxSWgsa+2Ld5SWSNbGtpPcmEM3Fl5ttMCNCKtNc0UE16oHwaPAIw==
  228. -----END RSA PRIVATE KEY-----
  229. """
  230. self.public_pem = """
  231. -----BEGIN PUBLIC KEY-----
  232. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6TtDhWGwzEOWZP6m/zHo
  233. ZnAPLABfetvoMPmxPGjFjtDuMRPvEvI1sbixZBjBtdnc5rTtHUUQ25Am3JzwPRGo
  234. 5laMGbj1pPyCPxlVi9LK82HQNX0BYK7tZtVfDHElQA7F4v3j9d3rad4O9/n+lyGI
  235. Q0tT7yQcBm2A8FEaP0bZYCLMjwMNWfaVLE8eXHyv+MfpNNLI9wttLxygKYM48I3N
  236. wsFuJgOa/KuodXaAmf8pJnx8t1WnnxvaYXFiUn/TxmhM/qhemPa6+0nqq+aWV5eT
  237. 7xn4K/ghLgNs09v6Yge0pmPl9Oz++bjJ+aKRnAmwCOY8/5U5EilAiUOeBoO9+8OX
  238. twIDAQAB
  239. -----END PUBLIC KEY-----
  240. """
  241. signed_token = tokens.signed_token_generator(self.private_pem,
  242. user_id=123)
  243. self.endpoint = Server(
  244. self.mock_validator,
  245. token_expires_in=self.expires_in,
  246. token_generator=signed_token,
  247. refresh_token_generator=tokens.random_token_generator
  248. )
  249. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  250. def test_authorization_grant(self):
  251. body = 'client_id=me&redirect_uri=http%3A%2F%2Fback.to%2Fme&grant_type=authorization_code&code=abc&scope=all+of+them'
  252. headers, body, status_code = self.endpoint.create_token_response(
  253. '', body=body)
  254. body = json.loads(body)
  255. token = {
  256. 'token_type': 'Bearer',
  257. 'expires_in': self.expires_in,
  258. 'access_token': body['access_token'],
  259. 'refresh_token': 'abc',
  260. 'scope': 'all of them'
  261. }
  262. self.assertEqual(body, token)
  263. body = 'client_id=me&redirect_uri=http%3A%2F%2Fback.to%2Fme&grant_type=authorization_code&code=abc'
  264. headers, body, status_code = self.endpoint.create_token_response(
  265. '', body=body)
  266. body = json.loads(body)
  267. token = {
  268. 'token_type': 'Bearer',
  269. 'expires_in': self.expires_in,
  270. 'access_token': body['access_token'],
  271. 'refresh_token': 'abc'
  272. }
  273. self.assertEqual(body, token)
  274. # try with additional custom variables
  275. body = 'client_id=me&redirect_uri=http%3A%2F%2Fback.to%2Fme&grant_type=authorization_code&code=abc&state=foobar'
  276. headers, body, status_code = self.endpoint.create_token_response(
  277. '', body=body)
  278. body = json.loads(body)
  279. token = {
  280. 'token_type': 'Bearer',
  281. 'expires_in': self.expires_in,
  282. 'access_token': body['access_token'],
  283. 'refresh_token': 'abc'
  284. }
  285. self.assertEqual(body, token)
  286. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  287. def test_password_grant(self):
  288. body = 'grant_type=password&username=a&password=hello&scope=all+of+them'
  289. headers, body, status_code = self.endpoint.create_token_response(
  290. '', body=body)
  291. body = json.loads(body)
  292. token = {
  293. 'token_type': 'Bearer',
  294. 'expires_in': self.expires_in,
  295. 'access_token': body['access_token'],
  296. 'refresh_token': 'abc',
  297. 'scope': 'all of them',
  298. }
  299. self.assertEqual(body, token)
  300. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  301. def test_scopes_and_user_id_stored_in_access_token(self):
  302. body = 'grant_type=password&username=a&password=hello&scope=all+of+them'
  303. headers, body, status_code = self.endpoint.create_token_response(
  304. '', body=body)
  305. access_token = json.loads(body)['access_token']
  306. claims = common.verify_signed_token(self.public_pem, access_token)
  307. self.assertEqual(claims['scope'], 'all of them')
  308. self.assertEqual(claims['user_id'], 123)
  309. @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc')
  310. def test_client_grant(self):
  311. body = 'grant_type=client_credentials&scope=all+of+them'
  312. headers, body, status_code = self.endpoint.create_token_response(
  313. '', body=body)
  314. body = json.loads(body)
  315. token = {
  316. 'token_type': 'Bearer',
  317. 'expires_in': self.expires_in,
  318. 'access_token': body['access_token'],
  319. 'scope': 'all of them',
  320. }
  321. self.assertEqual(body, token)
  322. def test_missing_type(self):
  323. _, body, _ = self.endpoint.create_token_response('', body='client_id=me&redirect_uri=http%3A%2F%2Fback.to%2Fme&code=abc')
  324. token = {'error': 'unsupported_grant_type'}
  325. self.assertEqual(json.loads(body), token)
  326. def test_invalid_type(self):
  327. body = 'client_id=me&redirect_uri=http%3A%2F%2Fback.to%2Fme&grant_type=invalid&code=abc'
  328. _, body, _ = self.endpoint.create_token_response('', body=body)
  329. token = {'error': 'unsupported_grant_type'}
  330. self.assertEqual(json.loads(body), token)
  331. class ResourceEndpointTest(TestCase):
  332. def setUp(self):
  333. self.mock_validator = mock.MagicMock()
  334. self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
  335. token = tokens.BearerToken(request_validator=self.mock_validator)
  336. self.endpoint = ResourceEndpoint(
  337. default_token='Bearer',
  338. token_types={'Bearer': token}
  339. )
  340. def test_defaults(self):
  341. uri = 'http://a.b/path?some=query'
  342. self.mock_validator.validate_bearer_token.return_value = False
  343. valid, request = self.endpoint.verify_request(uri)
  344. self.assertFalse(valid)
  345. self.assertEqual(request.token_type, 'Bearer')