|
@@ -0,0 +1,74 @@
|
|
|
+from time import time
|
|
|
+from unittest import mock
|
|
|
+
|
|
|
+from sentry.auth.authenticators.totp import TotpInterface
|
|
|
+from sentry.testutils.cases import TestCase
|
|
|
+from sentry.testutils.silo import control_silo_test
|
|
|
+
|
|
|
+
|
|
|
+@control_silo_test(stable=True)
|
|
|
+class TwoFactorTest(TestCase):
|
|
|
+ def test_not_pending_2fa(self):
|
|
|
+ resp = self.client.get("/auth/2fa/")
|
|
|
+ assert resp.status_code == 302
|
|
|
+ assert resp["Location"] == "/auth/login/"
|
|
|
+
|
|
|
+ def test_no_2fa_configured(self):
|
|
|
+ user = self.create_user()
|
|
|
+ self.login_as(user)
|
|
|
+
|
|
|
+ self.session["_pending_2fa"] = [user.id, time() - 2]
|
|
|
+ self.save_session()
|
|
|
+
|
|
|
+ resp = self.client.get("/auth/2fa/", follow=True)
|
|
|
+ assert resp.redirect_chain == [
|
|
|
+ ("/auth/login/", 302),
|
|
|
+ ("/organizations/new/", 302),
|
|
|
+ ]
|
|
|
+
|
|
|
+ def test_otp_challenge(self):
|
|
|
+ user = self.create_user()
|
|
|
+ interface = TotpInterface()
|
|
|
+ interface.enroll(user)
|
|
|
+
|
|
|
+ self.login_as(user)
|
|
|
+ self.session["_pending_2fa"] = [user.id, time() - 2]
|
|
|
+ self.save_session()
|
|
|
+
|
|
|
+ resp = self.client.get("/auth/2fa/")
|
|
|
+ assert resp.status_code == 200
|
|
|
+ self.assertTemplateUsed("sentry/twofactor.html")
|
|
|
+ assert "provide the access code" in resp.content.decode("utf8")
|
|
|
+
|
|
|
+ @mock.patch("sentry.auth.authenticators.TotpInterface.validate_otp", return_value=None)
|
|
|
+ @mock.patch("time.sleep")
|
|
|
+ def test_otp_submit_error(self, mock_sleep, mock_validate):
|
|
|
+ user = self.create_user()
|
|
|
+ interface = TotpInterface()
|
|
|
+ interface.enroll(user)
|
|
|
+
|
|
|
+ self.login_as(user)
|
|
|
+ self.session["_pending_2fa"] = [user.id, time() - 2]
|
|
|
+ self.save_session()
|
|
|
+
|
|
|
+ resp = self.client.post("/auth/2fa/", data={"otp": "123456"}, follow=True)
|
|
|
+ assert mock_validate.called
|
|
|
+ assert resp.status_code == 200
|
|
|
+ assert "Invalid confirmation code" in resp.content.decode("utf8")
|
|
|
+
|
|
|
+ @mock.patch("sentry.auth.authenticators.TotpInterface.validate_otp", return_value=True)
|
|
|
+ def test_otp_submit_success(self, mock_validate):
|
|
|
+ user = self.create_user()
|
|
|
+ interface = TotpInterface()
|
|
|
+ interface.enroll(user)
|
|
|
+
|
|
|
+ self.login_as(user)
|
|
|
+ self.session["_pending_2fa"] = [user.id, time() - 2]
|
|
|
+ self.save_session()
|
|
|
+
|
|
|
+ resp = self.client.post("/auth/2fa/", data={"otp": "123456"}, follow=True)
|
|
|
+ assert mock_validate.called
|
|
|
+ assert resp.redirect_chain == [
|
|
|
+ ("/auth/login/", 302),
|
|
|
+ ("/organizations/new/", 302),
|
|
|
+ ]
|