You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
14 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 SYLEAM
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. import json
  5. import logging
  6. from .common_test_controller import OAuthProviderControllerTransactionCase
  7. from .common_test_oauth_provider_controller import \
  8. TestOAuthProviderRefreshTokenController, \
  9. TestOAuthProviderAurhorizeController, \
  10. TestOAuthProviderTokeninfoController, \
  11. TestOAuthProviderUserinfoController, \
  12. TestOAuthProviderOtherinfoController, \
  13. TestOAuthProviderRevokeTokenController
  14. _logger = logging.getLogger(__name__)
  15. try:
  16. import oauthlib
  17. except ImportError:
  18. _logger.debug('Cannot `import oauthlib`.')
  19. class TestOAuthProviderController(
  20. OAuthProviderControllerTransactionCase,
  21. TestOAuthProviderRefreshTokenController,
  22. TestOAuthProviderAurhorizeController,
  23. TestOAuthProviderTokeninfoController,
  24. TestOAuthProviderUserinfoController,
  25. TestOAuthProviderOtherinfoController,
  26. TestOAuthProviderRevokeTokenController):
  27. def setUp(self):
  28. super(TestOAuthProviderController, self).setUp('web application')
  29. def new_code(self):
  30. # Configure the client to skip the authorization page
  31. self.client.skip_authorization = True
  32. # Call the authorize method with good values
  33. state = 'Some custom state'
  34. self.login('demo', 'demo')
  35. response = self.get_request('/oauth2/authorize', data={
  36. 'client_id': self.client.identifier,
  37. 'response_type': self.client.response_type,
  38. 'redirect_uri': self.redirect_uri_base,
  39. 'scope': self.client.scope_ids[0].code,
  40. 'state': state,
  41. })
  42. # A new authorization code should have been generated
  43. # We can safely pick the latest generated code here, because no other
  44. # code could have been generated during the test
  45. code = self.env['oauth.provider.authorization.code'].search([
  46. ('client_id', '=', self.client.id),
  47. ], order='id DESC', limit=1)
  48. # The response should be a redirect to the redirect URI, with the
  49. # authorization_code added as GET parameter
  50. self.assertEqual(response.status_code, 302)
  51. query_string = oauthlib.common.urlencode(
  52. {'state': state, 'code': code.code}.items())
  53. self.assertEqual(
  54. response.headers['Location'], '{uri_base}?{query_string}'.format(
  55. uri_base=self.redirect_uri_base, query_string=query_string))
  56. self.assertEqual(code.user_id, self.user)
  57. self.logout()
  58. return code
  59. def test_token_error_missing_session(self):
  60. """ Check /oauth2/token without any session set
  61. Must return an invalid_client_id error
  62. """
  63. response = self.post_request('/oauth2/token')
  64. self.assertEqual(response.status_code, 401)
  65. self.assertEqual(
  66. json.loads(response.data), {'error': 'invalid_client_id'})
  67. def test_token_error_missing_arguments(self):
  68. """ Check /oauth2/token without any argument
  69. Must return an invalid_client_id error
  70. """
  71. # Generate an authorization code to set the session
  72. self.new_code()
  73. response = self.post_request('/oauth2/token')
  74. self.assertEqual(response.status_code, 401)
  75. self.assertEqual(
  76. json.loads(response.data), {'error': 'invalid_client_id'})
  77. def test_token_error_wrong_grant_type(self):
  78. """ Check /oauth2/token with an invalid grant type
  79. Must return an invalid_client_id error
  80. """
  81. # Generate an authorization code to set the session
  82. self.new_code()
  83. response = self.post_request('/oauth2/token', data={
  84. 'grant_type': 'Wrong grant type',
  85. })
  86. self.assertEqual(response.status_code, 401)
  87. self.assertEqual(
  88. json.loads(response.data), {'error': 'invalid_client_id'})
  89. def test_token_error_missing_code(self):
  90. """ Check /oauth2/token without code
  91. Must return an invalid_client_id error
  92. """
  93. # Generate an authorization code to set the session
  94. self.new_code()
  95. response = self.post_request('/oauth2/token', data={
  96. 'grant_type': self.client.grant_type,
  97. })
  98. self.assertEqual(response.status_code, 401)
  99. self.assertEqual(
  100. json.loads(response.data), {'error': 'invalid_client_id'})
  101. def test_token_error_missing_client_id(self):
  102. """ Check /oauth2/token without client
  103. Must return an invalid_client_id error
  104. """
  105. # Generate an authorization code to set the session
  106. self.new_code()
  107. response = self.post_request('/oauth2/token', data={
  108. 'grant_type': self.client.grant_type,
  109. 'code': 'Wrong code',
  110. })
  111. self.assertEqual(response.status_code, 401)
  112. self.assertEqual(
  113. json.loads(response.data), {'error': 'invalid_client_id'})
  114. def test_token_error_wrong_client_identifier(self):
  115. """ Check /oauth2/token with a wrong client identifier
  116. Must return an invalid_client_id error
  117. """
  118. # Generate an authorization code to set the session
  119. self.new_code()
  120. response = self.post_request('/oauth2/token', data={
  121. 'grant_type': self.client.grant_type,
  122. 'client_id': 'Wrong client identifier',
  123. 'code': 'Wrong code',
  124. })
  125. self.assertEqual(response.status_code, 401)
  126. self.assertEqual(
  127. json.loads(response.data), {'error': 'invalid_client_id'})
  128. def test_token_error_wrong_code(self):
  129. """ Check /oauth2/token with a wrong code
  130. Must return an invalid_grant error
  131. """
  132. # Generate an authorization code to set the session
  133. self.new_code()
  134. response = self.post_request('/oauth2/token', data={
  135. 'grant_type': self.client.grant_type,
  136. 'client_id': self.client.identifier,
  137. 'code': 'Wrong code',
  138. })
  139. self.assertEqual(response.status_code, 401)
  140. self.assertEqual(json.loads(response.data), {'error': 'invalid_grant'})
  141. def test_token_error_missing_redirect_uri(self):
  142. """ Check /oauth2/token without redirect_uri
  143. Must return an access_denied error
  144. """
  145. # Generate an authorization code
  146. code = self.new_code()
  147. response = self.post_request('/oauth2/token', data={
  148. 'grant_type': self.client.grant_type,
  149. 'client_id': self.client.identifier,
  150. 'code': code.code,
  151. })
  152. self.assertEqual(response.status_code, 401)
  153. self.assertEqual(json.loads(response.data), {'error': 'access_denied'})
  154. def test_token_error_wrong_redirect_uri(self):
  155. """ Check /oauth2/token with a wrong redirect_uri
  156. Must return an access_denied error
  157. """
  158. # Generate an authorization code
  159. code = self.new_code()
  160. response = self.post_request('/oauth2/token', data={
  161. 'grant_type': self.client.grant_type,
  162. 'client_id': self.client.identifier,
  163. 'code': code.code,
  164. 'redirect_uri': 'Wrong redirect URI',
  165. })
  166. self.assertEqual(response.status_code, 401)
  167. self.assertEqual(json.loads(response.data), {'error': 'access_denied'})
  168. def test_token_error_wrong_client_id(self):
  169. """ Check /oauth2/token with a wrong client id
  170. Must return an invalid_client_id error
  171. """
  172. # Generate an authorization code
  173. code = self.new_code()
  174. response = self.post_request('/oauth2/token', data={
  175. 'grant_type': self.client.grant_type,
  176. 'client_id': 'Wrong client id',
  177. 'code': code.code,
  178. 'redirect_uri': self.redirect_uri_base,
  179. 'scope': self.client.scope_ids[0].code,
  180. })
  181. self.assertEqual(response.status_code, 401)
  182. self.assertEqual(
  183. json.loads(response.data), {'error': 'invalid_client_id'})
  184. def test_token_error_missing_refresh_token(self):
  185. """ Check /oauth2/token in refresh token mode without refresh token
  186. Must return an invalid_request error
  187. """
  188. # Generate an authorization code to set the session
  189. self.new_code()
  190. response = self.post_request('/oauth2/token', data={
  191. 'grant_type': 'refresh_token',
  192. 'client_id': self.client.identifier,
  193. 'redirect_uri': self.redirect_uri_base,
  194. 'scope': self.client.scope_ids[0].code,
  195. })
  196. self.assertEqual(response.status_code, 400)
  197. self.assertEqual(json.loads(response.data), {
  198. 'error': 'invalid_request',
  199. 'error_description': 'Missing refresh token parameter.',
  200. })
  201. def test_token_error_invalid_refresh_token(self):
  202. """ Check /oauth2/token in refresh token mode with an invalid refresh token
  203. Must return an invalid_grant error
  204. """
  205. # Generate an authorization code to set the session
  206. self.new_code()
  207. response = self.post_request('/oauth2/token', data={
  208. 'grant_type': 'refresh_token',
  209. 'client_id': self.client.identifier,
  210. 'redirect_uri': self.redirect_uri_base,
  211. 'scope': self.client.scope_ids[0].code,
  212. 'refresh_token': 'Wrong refresh token',
  213. })
  214. self.assertEqual(response.status_code, 401)
  215. self.assertEqual(json.loads(response.data), {'error': 'invalid_grant'})
  216. def test_authorize_skip_authorization(self):
  217. """ Call /oauth2/authorize while skipping the authorization page """
  218. # Configure the client to skip the authorization page
  219. self.client.skip_authorization = True
  220. # Call the authorize method with good values
  221. state = 'Some custom state'
  222. self.login('demo', 'demo')
  223. response = self.get_request('/oauth2/authorize', data={
  224. 'client_id': self.client.identifier,
  225. 'response_type': self.client.response_type,
  226. 'redirect_uri': self.redirect_uri_base,
  227. 'scope': self.client.scope_ids[0].code,
  228. 'state': state,
  229. })
  230. # A new authorization code should have been generated
  231. # We can safely pick the latest generated code here, because no other
  232. # code could have been generated during the test
  233. code = self.env['oauth.provider.authorization.code'].search([
  234. ('client_id', '=', self.client.id),
  235. ], order='id DESC', limit=1)
  236. # The response should be a redirect to the redirect URI, with the
  237. # authorization_code added as GET parameter
  238. self.assertEqual(response.status_code, 302)
  239. query_string = oauthlib.common.urlencode({
  240. 'state': state,
  241. 'code': code.code,
  242. }.items())
  243. self.assertEqual(
  244. response.headers['Location'], '{uri_base}?{query_string}'.format(
  245. uri_base=self.redirect_uri_base, query_string=query_string))
  246. self.assertEqual(code.user_id, self.user)
  247. def test_successful_token_retrieval(self):
  248. """ Check the full process for a WebApplication
  249. GET, then POST, token and informations retrieval
  250. """
  251. # Call the authorize method with good values to fill the session scopes
  252. # and credentials variables
  253. state = 'Some custom state'
  254. self.login('demo', 'demo')
  255. response = self.get_request('/oauth2/authorize', data={
  256. 'client_id': self.client.identifier,
  257. 'response_type': self.client.response_type,
  258. 'redirect_uri': self.redirect_uri_base,
  259. 'scope': self.client.scope_ids[0].code,
  260. 'state': state,
  261. })
  262. self.assertEqual(response.status_code, 200)
  263. self.assertTrue(self.client.name in response.data)
  264. self.assertTrue(self.client.scope_ids[0].name in response.data)
  265. self.assertTrue(self.client.scope_ids[0].description in response.data)
  266. # Then, call the POST route to validate the authorization
  267. response = self.post_request('/oauth2/authorize')
  268. # A new authorization code should have been generated
  269. # We can safely pick the latest generated code here, because no other
  270. # code could have been generated during the test
  271. code = self.env['oauth.provider.authorization.code'].search([
  272. ('client_id', '=', self.client.id),
  273. ], order='id DESC', limit=1)
  274. # The response should be a redirect to the redirect URI, with the
  275. # authorization_code added as GET parameter
  276. self.assertEqual(response.status_code, 302)
  277. query_string = oauthlib.common.urlencode({
  278. 'state': state,
  279. 'code': code.code,
  280. }.items())
  281. self.assertEqual(
  282. response.headers['Location'], '{uri_base}?{query_string}'.format(
  283. uri_base=self.redirect_uri_base, query_string=query_string))
  284. self.assertEqual(code.user_id, self.user)
  285. self.logout()
  286. # Now that the user vaidated the authorization, we can ask for a token,
  287. # using the returned code
  288. response = self.post_request('/oauth2/token', data={
  289. 'client_id': self.client.identifier,
  290. 'redirect_uri': self.redirect_uri_base,
  291. 'scope': self.client.scope_ids[0].code,
  292. 'code': code.code,
  293. 'grant_type': self.client.grant_type,
  294. })
  295. response_data = json.loads(response.data)
  296. # A new token should have been generated
  297. # We can safely pick the latest generated token here, because no other
  298. # token could have been generated during the test
  299. token = self.env['oauth.provider.token'].search([
  300. ('client_id', '=', self.client.id),
  301. ], order='id DESC', limit=1)
  302. self.assertEqual(response.status_code, 200)
  303. self.assertEqual(token.token, response_data['access_token'])
  304. self.assertEqual(token.token_type, response_data['token_type'])
  305. self.assertEqual(token.refresh_token, response_data['refresh_token'])
  306. self.assertEqual(token.scope_ids, code.scope_ids)
  307. self.assertEqual(token.user_id, self.user)