test_auth.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. from django.conf import settings
  5. from django.contrib.auth.models import User
  6. from django.db import IntegrityError
  7. from mock import ANY, Mock, patch
  8. from django_browserid.auth import AutoLoginBackend, BrowserIDBackend, default_username_algo
  9. from django_browserid.base import MockVerifier
  10. from django_browserid.tests import mock_browserid, TestCase
  11. try:
  12. from django.contrib.auth import get_user_model
  13. from django_browserid.tests.models import CustomUser
  14. except ImportError:
  15. get_user_model = False
  16. def new_user(email, username=None):
  17. """Creates a user with the specified email for testing."""
  18. if username is None:
  19. username = default_username_algo(email)
  20. return User.objects.create_user(username, email)
  21. class BrowserIDBackendTests(TestCase):
  22. def setUp(self):
  23. self.backend = BrowserIDBackend()
  24. self.verifier = Mock()
  25. self.backend.get_verifier = lambda: self.verifier
  26. def test_verify_failure(self):
  27. """If verification fails, return None."""
  28. self.verifier.verify.return_value = False
  29. self.assertEqual(self.backend.verify('asdf', 'qwer'), None)
  30. self.verifier.verify.assert_called_with('asdf', 'qwer')
  31. def test_verify_success(self):
  32. """
  33. If verification succeeds, return the email address from the
  34. verification result.
  35. """
  36. self.verifier.verify.return_value = Mock(email='bob@example.com')
  37. self.assertEqual(self.backend.verify('asdf', 'qwer'), 'bob@example.com')
  38. self.verifier.verify.assert_called_with('asdf', 'qwer')
  39. def test_verify_no_audience_request(self):
  40. """
  41. If no audience is provided but a request is, retrieve the
  42. audience from the request using get_audience.
  43. """
  44. request = Mock()
  45. with patch('django_browserid.auth.get_audience') as get_audience:
  46. self.backend.verify('asdf', request=request)
  47. get_audience.assert_called_with(request)
  48. self.verifier.verify.assert_called_with('asdf', get_audience.return_value)
  49. def test_verify_no_audience_no_assertion_no_service(self):
  50. """
  51. If the assertion isn't provided, or the audience and request
  52. aren't provided, return None.
  53. """
  54. self.assertEqual(self.backend.verify(audience='asdf'), None)
  55. self.assertEqual(self.backend.verify(assertion='asdf'), None)
  56. with patch('django_browserid.auth.get_audience') as get_audience:
  57. get_audience.return_value = None
  58. self.assertEqual(self.backend.verify('asdf', request=Mock()), None)
  59. def test_verify_kwargs(self):
  60. """Any extra kwargs should be passed to the verifier."""
  61. self.backend.verify('asdf', 'asdf', request='blah', foo='bar', baz=1)
  62. self.verifier.verify.assert_called_with('asdf', 'asdf', foo='bar', baz=1)
  63. def auth(self, verified_email=None, **kwargs):
  64. """
  65. Attempt to authenticate a user with BrowserIDBackend.
  66. If verified_email is None, verification will fail, otherwise it will
  67. pass and return the specified email.
  68. """
  69. self.backend.verify = Mock(return_value=verified_email)
  70. return self.backend.authenticate(assertion='asdf', audience='asdf', **kwargs)
  71. def test_duplicate_emails(self):
  72. """
  73. If there are two users with the same email address, return None.
  74. """
  75. new_user('a@example.com', 'test1')
  76. new_user('a@example.com', 'test2')
  77. self.assertTrue(self.auth('a@example.com') is None)
  78. def test_auth_success(self):
  79. """
  80. If a single user is found with the verified email, return an
  81. instance of their user object.
  82. """
  83. user = new_user('a@example.com')
  84. self.assertEqual(self.auth('a@example.com'), user)
  85. @patch.object(settings, 'BROWSERID_CREATE_USER', False)
  86. def test_no_create_user(self):
  87. """
  88. If user creation is disabled and no user is found, return None.
  89. """
  90. self.assertTrue(self.auth('a@example.com') is None)
  91. @patch.object(settings, 'BROWSERID_CREATE_USER', True)
  92. def test_create_user(self):
  93. """
  94. If user creation is enabled and no user is found, return a new
  95. User.
  96. """
  97. user = self.auth('a@example.com')
  98. self.assertTrue(user is not None)
  99. self.assertTrue(isinstance(user, User))
  100. self.assertEqual(user.email, 'a@example.com')
  101. @patch.object(settings, 'BROWSERID_CREATE_USER',
  102. 'django_browserid.tests.test_auth.new_user')
  103. @patch('django_browserid.tests.test_auth.new_user')
  104. def test_custom_create_user(self, create_user):
  105. """
  106. If user creation is enabled with a custom create function and no
  107. user is found, return the new user created with the custom
  108. function.
  109. """
  110. create_user.return_value = 'test'
  111. self.assertEqual(self.auth('a@example.com'), 'test')
  112. create_user.assert_called_with('a@example.com')
  113. @patch.object(settings, 'BROWSERID_USERNAME_ALGO')
  114. @patch.object(settings, 'BROWSERID_CREATE_USER', True)
  115. def test_custom_username_algorithm(self, username_algo):
  116. """If a custom username algorithm is specified, use it!"""
  117. username_algo.return_value = 'test'
  118. user = self.auth('a@b.com')
  119. self.assertEqual(user.username, 'test')
  120. @patch('django_browserid.auth.user_created')
  121. @patch.object(settings, 'BROWSERID_CREATE_USER', True)
  122. def test_user_created_signal(self, user_created):
  123. """
  124. Test that the user_created signal is called when a new user is
  125. created.
  126. """
  127. user = self.auth('a@b.com')
  128. user_created.send.assert_called_with(ANY, user=user)
  129. def test_get_user(self):
  130. """
  131. Check if user returned by BrowserIDBackend.get_user is correct.
  132. """
  133. user = new_user('a@example.com')
  134. backend = BrowserIDBackend()
  135. self.assertEqual(backend.get_user(user.pk), user)
  136. def test_overriding_valid_email(self):
  137. class PickyBackend(BrowserIDBackend):
  138. def is_valid_email(self, email):
  139. return email != 'a@example.com'
  140. new_user('a@example.com', 'test1')
  141. new_user('b@example.com', 'test2')
  142. with mock_browserid('a@example.com'):
  143. backend = PickyBackend()
  144. result = backend.authenticate(assertion='asdf', audience='asdf')
  145. self.assertTrue(not result)
  146. with mock_browserid('b@example.com'):
  147. backend = PickyBackend()
  148. result = backend.authenticate(assertion='asdf', audience='asdf')
  149. self.assertTrue(result)
  150. @patch('django_browserid.auth.logger')
  151. def test_create_user_integrity_error(self, logger):
  152. """
  153. If an IntegrityError is raised during user creation, attempt to
  154. re-fetch the user in case the user was created since we checked
  155. for the existing account.
  156. """
  157. backend = BrowserIDBackend()
  158. backend.User = Mock()
  159. error = IntegrityError()
  160. backend.User.objects.create_user.side_effect = error
  161. backend.User.objects.get.return_value = 'asdf'
  162. self.assertEqual(backend.create_user('a@example.com'), 'asdf')
  163. # If get raises a DoesNotExist exception, re-raise the original exception.
  164. backend.User.DoesNotExist = Exception
  165. backend.User.objects.get.side_effect = backend.User.DoesNotExist
  166. with self.assertRaises(IntegrityError) as e:
  167. backend.create_user('a@example.com')
  168. self.assertEqual(e.exception, error)
  169. def test_authenticate_verify_exception(self):
  170. """
  171. If the verifier raises an exception, log it as a warning and
  172. return None.
  173. """
  174. backend = BrowserIDBackend()
  175. verifier = Mock()
  176. exception = Exception()
  177. backend.get_verifier = lambda: verifier
  178. verifier.verify.side_effect = exception
  179. with patch('django_browserid.auth.logger') as logger:
  180. self.assertEqual(backend.authenticate('asdf', 'asdf'), None)
  181. logger.warn.assert_called_with(exception)
  182. if get_user_model:
  183. # Only run custom user model tests if we're using a version of Django that
  184. # supports it.
  185. @patch.object(settings, 'AUTH_USER_MODEL', 'tests.CustomUser')
  186. class CustomUserModelTests(TestCase):
  187. def _auth(self, backend=None, verified_email=None):
  188. if backend is None:
  189. backend = BrowserIDBackend()
  190. with mock_browserid(verified_email):
  191. return backend.authenticate(assertion='asdf', audience='asdf')
  192. def test_existing_user(self):
  193. """If a custom user exists with the given email, return them."""
  194. user = CustomUser.objects.create(email='a@test.com')
  195. authed_user = self._auth(verified_email='a@test.com')
  196. self.assertEqual(user, authed_user)
  197. @patch.object(settings, 'BROWSERID_CREATE_USER', True)
  198. def test_create_new_user(self):
  199. """
  200. If a custom user does not exist with the given email, create a new
  201. user and return them.
  202. """
  203. class CustomUserBrowserIDBackend(BrowserIDBackend):
  204. def create_user(self, email):
  205. return CustomUser.objects.create(email=email)
  206. user = self._auth(backend=CustomUserBrowserIDBackend(),
  207. verified_email='b@test.com')
  208. self.assertTrue(isinstance(user, CustomUser))
  209. self.assertEqual(user.email, 'b@test.com')
  210. class AutoLoginBackendTests(TestCase):
  211. def setUp(self):
  212. self.backend = AutoLoginBackend()
  213. def test_verify_with_email(self):
  214. """
  215. If BROWSERID_AUTOLOGIN_EMAIL is set, use it to auth the user.
  216. """
  217. with self.settings(BROWSERID_AUTOLOGIN_EMAIL='bob@example.com',
  218. BROWSERID_AUTOLOGIN_ENABLED=True):
  219. self.assertEqual(self.backend.verify(), 'bob@example.com')
  220. def test_verify_without_email(self):
  221. """
  222. If BROWSERID_AUTOLOGIN_EMAIL is not set, do not auth the user.
  223. """
  224. with self.settings(BROWSERID_AUTOLOGIN_EMAIL='', BROWSERID_AUTOLOGIN_ENABLED=True):
  225. del settings.BROWSERID_AUTOLOGIN_EMAIL
  226. self.assertEqual(self.backend.verify(), None)
  227. def test_verify_disabled(self):
  228. """
  229. If BROWSERID_AUTOLOGIN_ENABLED is False, do not auth the user
  230. in any case.
  231. """
  232. with self.settings(BROWSERID_AUTOLOGIN_EMAIL='', BROWSERID_AUTOLOGIN_ENABLED=False):
  233. del settings.BROWSERID_AUTOLOGIN_EMAIL
  234. self.assertEqual(self.backend.verify(), None)
  235. with self.settings(BROWSERID_AUTOLOGIN_EMAIL='bob@example.com',
  236. BROWSERID_AUTOLOGIN_ENABLED=False):
  237. self.assertEqual(self.backend.verify(), None)