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.

133 lines
4.2 KiB

  1. import time
  2. from unittest.mock import patch
  3. from django.test.utils import override_settings
  4. from hc.test import BaseTestCase
  5. @override_settings(RP_ID="testserver")
  6. class LoginWebAuthnTestCase(BaseTestCase):
  7. def setUp(self):
  8. super().setUp()
  9. # This is the user we're trying to authenticate
  10. session = self.client.session
  11. session["2fa_user"] = [self.alice.id, self.alice.email, (time.time()) + 300]
  12. session.save()
  13. self.url = "/accounts/login/two_factor/"
  14. self.checks_url = f"/projects/{self.project.code}/checks/"
  15. def test_it_shows_form(self):
  16. r = self.client.get(self.url)
  17. self.assertContains(r, "Waiting for security key")
  18. # It should put a "state" key in the session:
  19. self.assertIn("state", self.client.session)
  20. def test_it_requires_unauthenticated_user(self):
  21. self.client.login(username="[email protected]", password="password")
  22. r = self.client.get(self.url)
  23. self.assertEqual(r.status_code, 400)
  24. def test_it_rejects_changed_email(self):
  25. session = self.client.session
  26. session["2fa_user"] = [self.alice.id, "[email protected]", int(time.time())]
  27. session.save()
  28. r = self.client.get(self.url)
  29. self.assertEqual(r.status_code, 400)
  30. def test_it_rejects_old_timestamp(self):
  31. session = self.client.session
  32. session["2fa_user"] = [self.alice.id, self.alice.email, int(time.time()) - 310]
  33. session.save()
  34. r = self.client.get(self.url)
  35. self.assertRedirects(r, "/accounts/login/")
  36. @override_settings(RP_ID=None)
  37. def test_it_requires_rp_id(self):
  38. r = self.client.get(self.url)
  39. self.assertEqual(r.status_code, 500)
  40. @patch("hc.accounts.views._check_credential")
  41. def test_it_logs_in(self, mock_check_credential):
  42. mock_check_credential.return_value = True
  43. session = self.client.session
  44. session["state"] = "dummy-state"
  45. session.save()
  46. payload = {
  47. "name": "My New Key",
  48. "credential_id": "e30=",
  49. "client_data_json": "e30=",
  50. "authenticator_data": "e30=",
  51. "signature": "e30=",
  52. }
  53. r = self.client.post(self.url, payload)
  54. self.assertRedirects(r, self.checks_url)
  55. self.assertNotIn("state", self.client.session)
  56. self.assertNotIn("2fa_user_id", self.client.session)
  57. @patch("hc.accounts.views._check_credential")
  58. def test_it_redirects_after_login(self, mock_check_credential):
  59. mock_check_credential.return_value = True
  60. session = self.client.session
  61. session["state"] = "dummy-state"
  62. session.save()
  63. payload = {
  64. "name": "My New Key",
  65. "credential_id": "e30=",
  66. "client_data_json": "e30=",
  67. "authenticator_data": "e30=",
  68. "signature": "e30=",
  69. }
  70. url = self.url + "?next=" + self.channels_url
  71. r = self.client.post(url, payload)
  72. self.assertRedirects(r, self.channels_url)
  73. @patch("hc.accounts.views._check_credential")
  74. def test_it_handles_bad_base64(self, mock_check_credential):
  75. mock_check_credential.return_value = None
  76. session = self.client.session
  77. session["state"] = "dummy-state"
  78. session.save()
  79. payload = {
  80. "name": "My New Key",
  81. "credential_id": "this is not base64 data",
  82. "client_data_json": "e30=",
  83. "authenticator_data": "e30=",
  84. "signature": "e30=",
  85. }
  86. r = self.client.post(self.url, payload)
  87. self.assertEqual(r.status_code, 400)
  88. @patch("hc.accounts.views._check_credential")
  89. def test_it_handles_authentication_failure(self, mock_check_credential):
  90. mock_check_credential.return_value = None
  91. session = self.client.session
  92. session["state"] = "dummy-state"
  93. session.save()
  94. payload = {
  95. "name": "My New Key",
  96. "credential_id": "e30=",
  97. "client_data_json": "e30=",
  98. "authenticator_data": "e30=",
  99. "signature": "e30=",
  100. }
  101. r = self.client.post(self.url, payload)
  102. self.assertEqual(r.status_code, 400)