test_auth_backends.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. from __future__ import unicode_literals
  2. from datetime import date
  3. from django.conf import settings
  4. from django.contrib.auth.backends import ModelBackend
  5. from django.contrib.auth.models import User, Group, Permission, AnonymousUser
  6. from django.contrib.auth.tests.utils import skipIfCustomUser
  7. from django.contrib.auth.tests.custom_user import ExtensionUser, CustomPermissionsUser, CustomUser
  8. from django.contrib.contenttypes.models import ContentType
  9. from django.core.exceptions import ImproperlyConfigured, PermissionDenied
  10. from django.contrib.auth import authenticate, get_user
  11. from django.http import HttpRequest
  12. from django.test import TestCase, override_settings
  13. from django.contrib.auth.hashers import MD5PasswordHasher
  14. class CountingMD5PasswordHasher(MD5PasswordHasher):
  15. """Hasher that counts how many times it computes a hash."""
  16. calls = 0
  17. def encode(self, *args, **kwargs):
  18. type(self).calls += 1
  19. return super(CountingMD5PasswordHasher, self).encode(*args, **kwargs)
  20. class BaseModelBackendTest(object):
  21. """
  22. A base class for tests that need to validate the ModelBackend
  23. with different User models. Subclasses should define a class
  24. level UserModel attribute, and a create_users() method to
  25. construct two users for test purposes.
  26. """
  27. backend = 'django.contrib.auth.backends.ModelBackend'
  28. def setUp(self):
  29. self.curr_auth = settings.AUTHENTICATION_BACKENDS
  30. settings.AUTHENTICATION_BACKENDS = (self.backend,)
  31. self.create_users()
  32. def tearDown(self):
  33. settings.AUTHENTICATION_BACKENDS = self.curr_auth
  34. # The custom_perms test messes with ContentTypes, which will
  35. # be cached; flush the cache to ensure there are no side effects
  36. # Refs #14975, #14925
  37. ContentType.objects.clear_cache()
  38. def test_has_perm(self):
  39. user = self.UserModel._default_manager.get(pk=self.user.pk)
  40. self.assertEqual(user.has_perm('auth.test'), False)
  41. user.is_staff = True
  42. user.save()
  43. self.assertEqual(user.has_perm('auth.test'), False)
  44. user.is_superuser = True
  45. user.save()
  46. self.assertEqual(user.has_perm('auth.test'), True)
  47. user.is_staff = False
  48. user.is_superuser = False
  49. user.save()
  50. self.assertEqual(user.has_perm('auth.test'), False)
  51. user.is_staff = True
  52. user.is_superuser = True
  53. user.is_active = False
  54. user.save()
  55. self.assertEqual(user.has_perm('auth.test'), False)
  56. def test_custom_perms(self):
  57. user = self.UserModel._default_manager.get(pk=self.user.pk)
  58. content_type = ContentType.objects.get_for_model(Group)
  59. perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
  60. user.user_permissions.add(perm)
  61. user.save()
  62. # reloading user to purge the _perm_cache
  63. user = self.UserModel._default_manager.get(pk=self.user.pk)
  64. self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
  65. self.assertEqual(user.get_group_permissions(), set([]))
  66. self.assertEqual(user.has_module_perms('Group'), False)
  67. self.assertEqual(user.has_module_perms('auth'), True)
  68. perm = Permission.objects.create(name='test2', content_type=content_type, codename='test2')
  69. user.user_permissions.add(perm)
  70. user.save()
  71. perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
  72. user.user_permissions.add(perm)
  73. user.save()
  74. user = self.UserModel._default_manager.get(pk=self.user.pk)
  75. self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
  76. self.assertEqual(user.has_perm('test'), False)
  77. self.assertEqual(user.has_perm('auth.test'), True)
  78. self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
  79. perm = Permission.objects.create(name='test_group', content_type=content_type, codename='test_group')
  80. group = Group.objects.create(name='test_group')
  81. group.permissions.add(perm)
  82. group.save()
  83. user.groups.add(group)
  84. user = self.UserModel._default_manager.get(pk=self.user.pk)
  85. exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
  86. self.assertEqual(user.get_all_permissions(), exp)
  87. self.assertEqual(user.get_group_permissions(), set(['auth.test_group']))
  88. self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
  89. user = AnonymousUser()
  90. self.assertEqual(user.has_perm('test'), False)
  91. self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), False)
  92. def test_has_no_object_perm(self):
  93. """Regressiontest for #12462"""
  94. user = self.UserModel._default_manager.get(pk=self.user.pk)
  95. content_type = ContentType.objects.get_for_model(Group)
  96. perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
  97. user.user_permissions.add(perm)
  98. user.save()
  99. self.assertEqual(user.has_perm('auth.test', 'object'), False)
  100. self.assertEqual(user.get_all_permissions('object'), set([]))
  101. self.assertEqual(user.has_perm('auth.test'), True)
  102. self.assertEqual(user.get_all_permissions(), set(['auth.test']))
  103. def test_get_all_superuser_permissions(self):
  104. """A superuser has all permissions. Refs #14795."""
  105. user = self.UserModel._default_manager.get(pk=self.superuser.pk)
  106. self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
  107. @override_settings(PASSWORD_HASHERS=('django.contrib.auth.tests.test_auth_backends.CountingMD5PasswordHasher',))
  108. def test_authentication_timing(self):
  109. """Hasher is run once regardless of whether the user exists. Refs #20760."""
  110. # Re-set the password, because this tests overrides PASSWORD_HASHERS
  111. self.user.set_password('test')
  112. self.user.save()
  113. CountingMD5PasswordHasher.calls = 0
  114. username = getattr(self.user, self.UserModel.USERNAME_FIELD)
  115. authenticate(username=username, password='test')
  116. self.assertEqual(CountingMD5PasswordHasher.calls, 1)
  117. CountingMD5PasswordHasher.calls = 0
  118. authenticate(username='no_such_user', password='test')
  119. self.assertEqual(CountingMD5PasswordHasher.calls, 1)
  120. @skipIfCustomUser
  121. class ModelBackendTest(BaseModelBackendTest, TestCase):
  122. """
  123. Tests for the ModelBackend using the default User model.
  124. """
  125. UserModel = User
  126. def create_users(self):
  127. self.user = User.objects.create_user(
  128. username='test',
  129. email='test@example.com',
  130. password='test',
  131. )
  132. self.superuser = User.objects.create_superuser(
  133. username='test2',
  134. email='test2@example.com',
  135. password='test',
  136. )
  137. @override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
  138. class ExtensionUserModelBackendTest(BaseModelBackendTest, TestCase):
  139. """
  140. Tests for the ModelBackend using the custom ExtensionUser model.
  141. This isn't a perfect test, because both the User and ExtensionUser are
  142. synchronized to the database, which wouldn't ordinary happen in
  143. production. As a result, it doesn't catch errors caused by the non-
  144. existence of the User table.
  145. The specific problem is queries on .filter(groups__user) et al, which
  146. makes an implicit assumption that the user model is called 'User'. In
  147. production, the auth.User table won't exist, so the requested join
  148. won't exist either; in testing, the auth.User *does* exist, and
  149. so does the join. However, the join table won't contain any useful
  150. data; for testing, we check that the data we expect actually does exist.
  151. """
  152. UserModel = ExtensionUser
  153. def create_users(self):
  154. self.user = ExtensionUser._default_manager.create_user(
  155. username='test',
  156. email='test@example.com',
  157. password='test',
  158. date_of_birth=date(2006, 4, 25)
  159. )
  160. self.superuser = ExtensionUser._default_manager.create_superuser(
  161. username='test2',
  162. email='test2@example.com',
  163. password='test',
  164. date_of_birth=date(1976, 11, 8)
  165. )
  166. @override_settings(AUTH_USER_MODEL='auth.CustomPermissionsUser')
  167. class CustomPermissionsUserModelBackendTest(BaseModelBackendTest, TestCase):
  168. """
  169. Tests for the ModelBackend using the CustomPermissionsUser model.
  170. As with the ExtensionUser test, this isn't a perfect test, because both
  171. the User and CustomPermissionsUser are synchronized to the database,
  172. which wouldn't ordinary happen in production.
  173. """
  174. UserModel = CustomPermissionsUser
  175. def create_users(self):
  176. self.user = CustomPermissionsUser._default_manager.create_user(
  177. email='test@example.com',
  178. password='test',
  179. date_of_birth=date(2006, 4, 25)
  180. )
  181. self.superuser = CustomPermissionsUser._default_manager.create_superuser(
  182. email='test2@example.com',
  183. password='test',
  184. date_of_birth=date(1976, 11, 8)
  185. )
  186. @override_settings(AUTH_USER_MODEL='auth.CustomUser')
  187. class CustomUserModelBackendAuthenticateTest(TestCase):
  188. """
  189. Tests that the model backend can accept a credentials kwarg labeled with
  190. custom user model's USERNAME_FIELD.
  191. """
  192. def test_authenticate(self):
  193. test_user = CustomUser._default_manager.create_user(
  194. email='test@example.com',
  195. password='test',
  196. date_of_birth=date(2006, 4, 25)
  197. )
  198. authenticated_user = authenticate(email='test@example.com', password='test')
  199. self.assertEqual(test_user, authenticated_user)
  200. class TestObj(object):
  201. pass
  202. class SimpleRowlevelBackend(object):
  203. def has_perm(self, user, perm, obj=None):
  204. if not obj:
  205. return # We only support row level perms
  206. if isinstance(obj, TestObj):
  207. if user.username == 'test2':
  208. return True
  209. elif user.is_anonymous() and perm == 'anon':
  210. return True
  211. elif not user.is_active and perm == 'inactive':
  212. return True
  213. return False
  214. def has_module_perms(self, user, app_label):
  215. if not user.is_anonymous() and not user.is_active:
  216. return False
  217. return app_label == "app1"
  218. def get_all_permissions(self, user, obj=None):
  219. if not obj:
  220. return [] # We only support row level perms
  221. if not isinstance(obj, TestObj):
  222. return ['none']
  223. if user.is_anonymous():
  224. return ['anon']
  225. if user.username == 'test2':
  226. return ['simple', 'advanced']
  227. else:
  228. return ['simple']
  229. def get_group_permissions(self, user, obj=None):
  230. if not obj:
  231. return # We only support row level perms
  232. if not isinstance(obj, TestObj):
  233. return ['none']
  234. if 'test_group' in [group.name for group in user.groups.all()]:
  235. return ['group_perm']
  236. else:
  237. return ['none']
  238. @skipIfCustomUser
  239. class RowlevelBackendTest(TestCase):
  240. """
  241. Tests for auth backend that supports object level permissions
  242. """
  243. backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend'
  244. def setUp(self):
  245. self.curr_auth = settings.AUTHENTICATION_BACKENDS
  246. settings.AUTHENTICATION_BACKENDS = tuple(self.curr_auth) + (self.backend,)
  247. self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
  248. self.user2 = User.objects.create_user('test2', 'test2@example.com', 'test')
  249. self.user3 = User.objects.create_user('test3', 'test3@example.com', 'test')
  250. def tearDown(self):
  251. settings.AUTHENTICATION_BACKENDS = self.curr_auth
  252. # The get_group_permissions test messes with ContentTypes, which will
  253. # be cached; flush the cache to ensure there are no side effects
  254. # Refs #14975, #14925
  255. ContentType.objects.clear_cache()
  256. def test_has_perm(self):
  257. self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
  258. self.assertEqual(self.user2.has_perm('perm', TestObj()), True)
  259. self.assertEqual(self.user2.has_perm('perm'), False)
  260. self.assertEqual(self.user2.has_perms(['simple', 'advanced'], TestObj()), True)
  261. self.assertEqual(self.user3.has_perm('perm', TestObj()), False)
  262. self.assertEqual(self.user3.has_perm('anon', TestObj()), False)
  263. self.assertEqual(self.user3.has_perms(['simple', 'advanced'], TestObj()), False)
  264. def test_get_all_permissions(self):
  265. self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['simple']))
  266. self.assertEqual(self.user2.get_all_permissions(TestObj()), set(['simple', 'advanced']))
  267. self.assertEqual(self.user2.get_all_permissions(), set([]))
  268. def test_get_group_permissions(self):
  269. group = Group.objects.create(name='test_group')
  270. self.user3.groups.add(group)
  271. self.assertEqual(self.user3.get_group_permissions(TestObj()), set(['group_perm']))
  272. class AnonymousUserBackendTest(TestCase):
  273. """
  274. Tests for AnonymousUser delegating to backend.
  275. """
  276. backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend'
  277. def setUp(self):
  278. self.curr_auth = settings.AUTHENTICATION_BACKENDS
  279. settings.AUTHENTICATION_BACKENDS = (self.backend,)
  280. self.user1 = AnonymousUser()
  281. def tearDown(self):
  282. settings.AUTHENTICATION_BACKENDS = self.curr_auth
  283. def test_has_perm(self):
  284. self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
  285. self.assertEqual(self.user1.has_perm('anon', TestObj()), True)
  286. def test_has_perms(self):
  287. self.assertEqual(self.user1.has_perms(['anon'], TestObj()), True)
  288. self.assertEqual(self.user1.has_perms(['anon', 'perm'], TestObj()), False)
  289. def test_has_module_perms(self):
  290. self.assertEqual(self.user1.has_module_perms("app1"), True)
  291. self.assertEqual(self.user1.has_module_perms("app2"), False)
  292. def test_get_all_permissions(self):
  293. self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['anon']))
  294. @skipIfCustomUser
  295. @override_settings(AUTHENTICATION_BACKENDS=[])
  296. class NoBackendsTest(TestCase):
  297. """
  298. Tests that an appropriate error is raised if no auth backends are provided.
  299. """
  300. def setUp(self):
  301. self.user = User.objects.create_user('test', 'test@example.com', 'test')
  302. def test_raises_exception(self):
  303. self.assertRaises(ImproperlyConfigured, self.user.has_perm, ('perm', TestObj(),))
  304. @skipIfCustomUser
  305. class InActiveUserBackendTest(TestCase):
  306. """
  307. Tests for an inactive user
  308. """
  309. backend = 'django.contrib.auth.tests.test_auth_backends.SimpleRowlevelBackend'
  310. def setUp(self):
  311. self.curr_auth = settings.AUTHENTICATION_BACKENDS
  312. settings.AUTHENTICATION_BACKENDS = (self.backend,)
  313. self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
  314. self.user1.is_active = False
  315. self.user1.save()
  316. def tearDown(self):
  317. settings.AUTHENTICATION_BACKENDS = self.curr_auth
  318. def test_has_perm(self):
  319. self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
  320. self.assertEqual(self.user1.has_perm('inactive', TestObj()), True)
  321. def test_has_module_perms(self):
  322. self.assertEqual(self.user1.has_module_perms("app1"), False)
  323. self.assertEqual(self.user1.has_module_perms("app2"), False)
  324. class PermissionDeniedBackend(object):
  325. """
  326. Always raises PermissionDenied.
  327. """
  328. supports_object_permissions = True
  329. supports_anonymous_user = True
  330. supports_inactive_user = True
  331. def authenticate(self, username=None, password=None):
  332. raise PermissionDenied
  333. @skipIfCustomUser
  334. class PermissionDeniedBackendTest(TestCase):
  335. """
  336. Tests that other backends are not checked once a backend raises PermissionDenied
  337. """
  338. backend = 'django.contrib.auth.tests.test_auth_backends.PermissionDeniedBackend'
  339. def setUp(self):
  340. self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
  341. self.user1.save()
  342. @override_settings(AUTHENTICATION_BACKENDS=(backend, ) +
  343. tuple(settings.AUTHENTICATION_BACKENDS))
  344. def test_permission_denied(self):
  345. "user is not authenticated after a backend raises permission denied #2550"
  346. self.assertEqual(authenticate(username='test', password='test'), None)
  347. @override_settings(AUTHENTICATION_BACKENDS=tuple(
  348. settings.AUTHENTICATION_BACKENDS) + (backend, ))
  349. def test_authenticates(self):
  350. self.assertEqual(authenticate(username='test', password='test'), self.user1)
  351. class NewModelBackend(ModelBackend):
  352. pass
  353. @skipIfCustomUser
  354. class ChangedBackendSettingsTest(TestCase):
  355. """
  356. Tests for changes in the settings.AUTHENTICATION_BACKENDS
  357. """
  358. backend = 'django.contrib.auth.tests.test_auth_backends.NewModelBackend'
  359. TEST_USERNAME = 'test_user'
  360. TEST_PASSWORD = 'test_password'
  361. TEST_EMAIL = 'test@example.com'
  362. def setUp(self):
  363. User.objects.create_user(self.TEST_USERNAME,
  364. self.TEST_EMAIL,
  365. self.TEST_PASSWORD)
  366. @override_settings(AUTHENTICATION_BACKENDS=(backend, ))
  367. def test_changed_backend_settings(self):
  368. """
  369. Tests that removing a backend configured in AUTHENTICATION_BACKENDS
  370. make already logged-in users disconnect.
  371. """
  372. # Get a session for the test user
  373. self.assertTrue(self.client.login(
  374. username=self.TEST_USERNAME,
  375. password=self.TEST_PASSWORD)
  376. )
  377. # Prepare a request object
  378. request = HttpRequest()
  379. request.session = self.client.session
  380. # Remove NewModelBackend
  381. with self.settings(AUTHENTICATION_BACKENDS=(
  382. 'django.contrib.auth.backends.ModelBackend',)):
  383. # Get the user from the request
  384. user = get_user(request)
  385. # Assert that the user retrieval is successful and the user is
  386. # anonymous as the backend is not longer available.
  387. self.assertIsNotNone(user)
  388. self.assertTrue(user.is_anonymous())
  389. class TypeErrorBackend(object):
  390. """
  391. Always raises TypeError.
  392. """
  393. supports_object_permissions = True
  394. supports_anonymous_user = True
  395. supports_inactive_user = True
  396. def authenticate(self, username=None, password=None):
  397. raise TypeError
  398. @skipIfCustomUser
  399. class TypeErrorBackendTest(TestCase):
  400. """
  401. Tests that a TypeError within a backend is propagated properly.
  402. Regression test for ticket #18171
  403. """
  404. backend = 'django.contrib.auth.tests.test_auth_backends.TypeErrorBackend'
  405. def setUp(self):
  406. self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
  407. @override_settings(AUTHENTICATION_BACKENDS=(backend, ))
  408. def test_type_error_raised(self):
  409. self.assertRaises(TypeError, authenticate, username='test', password='test')
  410. @skipIfCustomUser
  411. class ImproperlyConfiguredUserModelTest(TestCase):
  412. """
  413. Tests that an exception from within get_user_model is propagated and doesn't
  414. raise an UnboundLocalError.
  415. Regression test for ticket #21439
  416. """
  417. def setUp(self):
  418. self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
  419. self.client.login(
  420. username='test',
  421. password='test'
  422. )
  423. @override_settings(AUTH_USER_MODEL='thismodel.doesntexist')
  424. def test_does_not_shadow_exception(self):
  425. # Prepare a request object
  426. request = HttpRequest()
  427. request.session = self.client.session
  428. self.assertRaises(ImproperlyConfigured, get_user, request)