test_auth_login.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. from datetime import timedelta
  2. from functools import cached_property
  3. from unittest import mock
  4. from urllib.parse import quote as urlquote
  5. from urllib.parse import urlencode
  6. import pytest
  7. from django.conf import settings
  8. from django.test import override_settings
  9. from django.urls import reverse
  10. from django.utils import timezone
  11. from sentry import newsletter
  12. from sentry.auth.authenticators.recovery_code import RecoveryCodeInterface
  13. from sentry.auth.authenticators.totp import TotpInterface
  14. from sentry.models.authprovider import AuthProvider
  15. from sentry.models.organization import Organization
  16. from sentry.models.organizationmember import OrganizationMember
  17. from sentry.newsletter.dummy import DummyNewsletter
  18. from sentry.receivers import create_default_projects
  19. from sentry.silo.base import SiloMode
  20. from sentry.testutils.cases import TestCase
  21. from sentry.testutils.helpers.datetime import freeze_time
  22. from sentry.testutils.helpers.features import with_feature
  23. from sentry.testutils.hybrid_cloud import HybridCloudTestMixin
  24. from sentry.testutils.silo import assume_test_silo_mode, control_silo_test
  25. from sentry.users.models.user import User
  26. from sentry.utils import json
  27. # TODO(dcramer): need tests for SSO behavior and single org behavior
  28. @control_silo_test
  29. class AuthLoginTest(TestCase, HybridCloudTestMixin):
  30. @cached_property
  31. def path(self):
  32. return reverse("sentry-login")
  33. def allow_registration(self):
  34. return self.options({"auth.allow-registration": True})
  35. def test_renders_correct_template(self):
  36. resp = self.client.get(self.path)
  37. assert resp.status_code == 200
  38. self.assertTemplateUsed("sentry/login.html")
  39. def test_cannot_request_access(self):
  40. resp = self.client.get(self.path)
  41. assert resp.status_code == 200
  42. assert resp.context["join_request_link"] is None
  43. def test_renders_session_expire_message(self):
  44. self.client.cookies["session_expired"] = "1"
  45. resp = self.client.get(self.path)
  46. assert resp.status_code == 200
  47. self.assertTemplateUsed(resp, "sentry/login.html")
  48. messages = list(resp.context["messages"])
  49. assert len(messages) == 1
  50. assert messages[0].message == "Your session has expired."
  51. def test_login_invalid_password(self):
  52. # load it once for test cookie
  53. self.client.get(self.path)
  54. resp = self.client.post(
  55. self.path, {"username": self.user.username, "password": "bizbar", "op": "login"}
  56. )
  57. assert resp.status_code == 200
  58. assert resp.context["login_form"].errors["__all__"] == [
  59. "Please enter a correct username and password. Note that both fields may be case-sensitive."
  60. ]
  61. @override_settings(SENTRY_SELF_HOSTED=False)
  62. def test_login_ratelimited_ip_gets(self):
  63. url = reverse("sentry-login")
  64. with freeze_time("2000-01-01"):
  65. for _ in range(25):
  66. self.client.get(url)
  67. resp = self.client.get(url)
  68. assert resp.status_code == 429
  69. def test_login_ratelimited_user(self):
  70. self.client.get(self.path)
  71. # Make sure user gets ratelimited
  72. for i in range(5):
  73. self.client.post(
  74. self.path,
  75. {"username": self.user.username, "password": "wront_password", "op": "login"},
  76. follow=True,
  77. )
  78. resp = self.client.post(
  79. self.path,
  80. {"username": self.user.username, "password": "admin", "op": "login"},
  81. follow=True,
  82. )
  83. assert resp.status_code == 200
  84. assert resp.redirect_chain == []
  85. assert (
  86. "You have made too many login attempts. Please try again later."
  87. in resp.content.decode()
  88. )
  89. def test_login_valid_credentials(self):
  90. # load it once for test cookie
  91. self.client.get(self.path)
  92. resp = self.client.post(
  93. self.path,
  94. {"username": self.user.username, "password": "admin", "op": "login"},
  95. follow=True,
  96. )
  97. assert resp.status_code == 200
  98. assert resp.redirect_chain == [
  99. (reverse("sentry-login"), 302),
  100. ("/organizations/new/", 302),
  101. ]
  102. def test_login_valid_credentials_with_org(self):
  103. org = self.create_organization(owner=self.user)
  104. # load it once for test cookie
  105. self.client.get(self.path)
  106. resp = self.client.post(
  107. self.path,
  108. {"username": self.user.username, "password": "admin", "op": "login"},
  109. follow=True,
  110. )
  111. assert resp.status_code == 200
  112. assert resp.redirect_chain == [
  113. (reverse("sentry-login"), 302),
  114. (f"/organizations/{org.slug}/issues/", 302),
  115. ]
  116. def test_login_valid_credentials_2fa_redirect(self):
  117. user = self.create_user("bar@example.com")
  118. RecoveryCodeInterface().enroll(user)
  119. TotpInterface().enroll(user)
  120. self.create_member(organization=self.organization, user=user)
  121. self.client.get(self.path)
  122. resp = self.client.post(
  123. self.path,
  124. {"username": user.username, "password": "admin", "op": "login"},
  125. follow=True,
  126. )
  127. assert resp.status_code == 200
  128. assert resp.redirect_chain == [(reverse("sentry-2fa-dialog"), 302)]
  129. with mock.patch("sentry.auth.authenticators.TotpInterface.validate_otp", return_value=True):
  130. resp = self.client.post(reverse("sentry-2fa-dialog"), {"otp": "something"}, follow=True)
  131. assert resp.status_code == 200
  132. assert resp.redirect_chain == [
  133. (reverse("sentry-login"), 302),
  134. ("/organizations/baz/issues/", 302),
  135. ]
  136. @with_feature("system:multi-region")
  137. def test_login_valid_credentials_with_org_and_customer_domains(self):
  138. org = self.create_organization(owner=self.user)
  139. # load it once for test cookie
  140. self.client.get(self.path)
  141. resp = self.client.post(
  142. self.path,
  143. {"username": self.user.username, "password": "admin", "op": "login"},
  144. follow=True,
  145. )
  146. assert resp.status_code == 200
  147. assert resp.redirect_chain == [
  148. (f"http://{org.slug}.testserver/auth/login/", 302),
  149. (f"http://{org.slug}.testserver/issues/", 302),
  150. ]
  151. def test_registration_disabled(self):
  152. with self.feature({"auth:register": False}), self.allow_registration():
  153. resp = self.client.get(self.path)
  154. assert resp.context["register_form"] is None
  155. @mock.patch("sentry.analytics.record")
  156. def test_registration_valid(self, mock_record):
  157. with self.feature("auth:register"), self.allow_registration():
  158. resp = self.client.post(
  159. self.path,
  160. {
  161. "username": "test-a-really-long-email-address@example.com",
  162. "password": "foobar",
  163. "name": "Foo Bar",
  164. "op": "register",
  165. },
  166. )
  167. assert resp.status_code == 302, (
  168. resp.context["register_form"].errors if resp.status_code == 200 else None
  169. )
  170. frontend_events = {"event_name": "Sign Up"}
  171. marketing_query = urlencode({"frontend_events": json.dumps(frontend_events)})
  172. assert marketing_query in resp.headers["Location"]
  173. user = User.objects.get(username="test-a-really-long-email-address@example.com")
  174. assert user.email == "test-a-really-long-email-address@example.com"
  175. assert user.check_password("foobar")
  176. assert user.name == "Foo Bar"
  177. with assume_test_silo_mode(SiloMode.REGION):
  178. assert not OrganizationMember.objects.filter(user_id=user.id).exists()
  179. signup_record = [r for r in mock_record.call_args_list if r[0][0] == "user.signup"]
  180. assert signup_record == [
  181. mock.call(
  182. "user.signup",
  183. user_id=user.id,
  184. source="register-form",
  185. provider=None,
  186. referrer="in-app",
  187. )
  188. ]
  189. @override_settings(SENTRY_SINGLE_ORGANIZATION=True)
  190. def test_registration_single_org(self):
  191. with assume_test_silo_mode(SiloMode.MONOLITH):
  192. create_default_projects()
  193. with self.feature("auth:register"), self.allow_registration():
  194. resp = self.client.post(
  195. self.path,
  196. {
  197. "username": "test-a-really-long-email-address@example.com",
  198. "password": "foobar",
  199. "name": "Foo Bar",
  200. "op": "register",
  201. },
  202. )
  203. assert resp.status_code == 302, (
  204. resp.context["register_form"].errors if resp.status_code == 200 else None
  205. )
  206. user = User.objects.get(username="test-a-really-long-email-address@example.com")
  207. # User is part of the default org
  208. with assume_test_silo_mode(SiloMode.REGION):
  209. default_org = Organization.get_default()
  210. org_member = OrganizationMember.objects.get(
  211. organization_id=default_org.id, user_id=user.id
  212. )
  213. assert org_member.role == default_org.default_role
  214. self.assert_org_member_mapping(org_member=org_member)
  215. @override_settings(SENTRY_SINGLE_ORGANIZATION=True)
  216. @mock.patch("sentry.web.frontend.auth_login.ApiInviteHelper.from_session")
  217. def test_registration_single_org_with_invite(self, from_session):
  218. with assume_test_silo_mode(SiloMode.MONOLITH):
  219. create_default_projects()
  220. self.session["can_register"] = True
  221. self.save_session()
  222. self.client.get(self.path)
  223. invite_helper = mock.Mock(valid_request=True, organization_id=self.organization.id)
  224. from_session.return_value = invite_helper
  225. resp = self.client.post(
  226. self.path,
  227. {
  228. "username": "test@example.com",
  229. "password": "foobar",
  230. "name": "Foo Bar",
  231. "op": "register",
  232. },
  233. )
  234. user = User.objects.get(username="test@example.com")
  235. # An organization member should NOT have been created, even though
  236. # we're in single org mode, accepting the invite will handle that
  237. # (which we assert next)
  238. with assume_test_silo_mode(SiloMode.REGION):
  239. assert not OrganizationMember.objects.filter(user_id=user.id).exists()
  240. # Invitation was accepted
  241. assert len(invite_helper.accept_invite.mock_calls) == 1
  242. assert resp.status_code == 302
  243. assert "/organizations/new/" in resp["Location"]
  244. def test_register_renders_correct_template(self):
  245. with self.allow_registration():
  246. register_path = reverse("sentry-register")
  247. resp = self.client.get(register_path)
  248. assert resp.status_code == 200
  249. assert resp.context["op"] == "register"
  250. self.assertTemplateUsed("sentry/login.html")
  251. def test_register_prefills_invite_email(self):
  252. self.session["invite_email"] = "foo@example.com"
  253. self.session["can_register"] = True
  254. self.save_session()
  255. register_path = reverse("sentry-register")
  256. resp = self.client.get(register_path)
  257. assert resp.status_code == 200
  258. assert resp.context["op"] == "register"
  259. assert resp.context["register_form"].initial["username"] == "foo@example.com"
  260. self.assertTemplateUsed("sentry/login.html")
  261. @mock.patch("sentry.web.frontend.auth_login.ApiInviteHelper.from_session")
  262. def test_register_accepts_invite(self, from_session):
  263. self.session["can_register"] = True
  264. self.save_session()
  265. self.client.get(self.path)
  266. invite_helper = mock.Mock(valid_request=True, organization_id=self.organization.id)
  267. from_session.return_value = invite_helper
  268. resp = self.client.post(
  269. self.path,
  270. {
  271. "username": "test@example.com",
  272. "password": "foobar",
  273. "name": "Foo Bar",
  274. "op": "register",
  275. },
  276. )
  277. assert resp.status_code == 302
  278. assert len(invite_helper.accept_invite.mock_calls) == 1
  279. def test_register_new_user_accepts_invite_using_session(self):
  280. invite = self.create_member(
  281. email="member@example.com",
  282. token="abcdef",
  283. token_expires_at=timezone.now() + timedelta(hours=24),
  284. organization_id=self.organization.id,
  285. )
  286. self.session["can_register"] = True
  287. self.session["invite_token"] = invite.token
  288. self.session["invite_member_id"] = invite.id
  289. self.session["invite_organization_id"] = invite.organization_id
  290. self.save_session()
  291. self.client.get(self.path)
  292. resp = self.client.post(
  293. self.path,
  294. {
  295. "username": "member@example.com",
  296. "password": "foobar",
  297. "name": "Foo Bar",
  298. "op": "register",
  299. },
  300. )
  301. assert resp.status_code == 302
  302. assert f"/organizations/{self.organization.slug}/issues/" in resp["Location"]
  303. invite.refresh_from_db()
  304. assert invite.user_id
  305. assert invite.token is None
  306. assert User.objects.get(id=invite.user_id).username == "member@example.com"
  307. def test_redirects_to_relative_next_url(self):
  308. next = "/welcome"
  309. self.client.get(self.path + "?next=" + next)
  310. resp = self.client.post(
  311. self.path, {"username": self.user.username, "password": "admin", "op": "login"}
  312. )
  313. assert resp.status_code == 302
  314. assert resp.get("Location", "").endswith(next)
  315. def test_doesnt_redirect_to_external_next_url(self):
  316. next = "http://example.com"
  317. self.client.get(self.path + "?next=" + urlquote(next))
  318. resp = self.client.post(
  319. self.path,
  320. {"username": self.user.username, "password": "admin", "op": "login"},
  321. follow=True,
  322. )
  323. assert resp.redirect_chain == [
  324. (reverse("sentry-login"), 302),
  325. ("/organizations/new/", 302),
  326. ]
  327. def test_redirects_already_authed_non_superuser(self):
  328. self.user.update(is_superuser=False)
  329. self.login_as(self.user)
  330. with self.feature("organizations:create"):
  331. resp = self.client.get(self.path)
  332. self.assertRedirects(resp, "/organizations/new/")
  333. def test_redirects_authenticated_user_to_custom_next_url(self):
  334. self.user.update(is_superuser=False)
  335. self.login_as(self.user)
  336. resp = self.client.get(self.path + "?next=testserver")
  337. assert resp.status_code == 302
  338. assert resp.get("Location", "").endswith("testserver")
  339. def test_redirect_superuser(self):
  340. self.login_as(self.user, superuser=False)
  341. resp = self.client.get(self.path)
  342. with self.feature("organizations:create"):
  343. resp = self.client.get(self.path)
  344. self.assertRedirects(resp, "/organizations/new/")
  345. self.login_as(self.user, superuser=True)
  346. resp = self.client.get(self.path)
  347. with self.feature("organizations:create"):
  348. resp = self.client.get(self.path)
  349. self.assertRedirects(resp, "/organizations/new/")
  350. @override_settings(
  351. AUTH_PASSWORD_VALIDATORS=[
  352. {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}
  353. ]
  354. )
  355. def test_unable_to_set_weak_password_via_registration_form(self):
  356. with self.feature("auth:register"), self.options({"auth.allow-registration": True}):
  357. resp = self.client.post(
  358. self.path,
  359. {
  360. "username": "hello@example.com",
  361. "password": "hello@example.com",
  362. "name": "Hello World",
  363. "op": "register",
  364. },
  365. )
  366. assert resp.status_code == 200
  367. assert b"The password is too similar to the username." in resp.content
  368. @pytest.mark.skipif(
  369. settings.SENTRY_NEWSLETTER != "sentry.newsletter.dummy.DummyNewsletter",
  370. reason="Requires DummyNewsletter.",
  371. )
  372. @control_silo_test
  373. class AuthLoginNewsletterTest(TestCase):
  374. @cached_property
  375. def path(self):
  376. return reverse("sentry-login")
  377. @pytest.fixture(autouse=True)
  378. def enable_newsletter(self):
  379. with newsletter.backend.test_only__downcast_to(DummyNewsletter).enable():
  380. yield
  381. def test_registration_requires_subscribe_choice_with_newsletter(self):
  382. with self.feature("auth:register"), self.options({"auth.allow-registration": True}):
  383. resp = self.client.post(
  384. self.path,
  385. {
  386. "username": "test-a-really-long-email-address@example.com",
  387. "password": "foobar",
  388. "name": "Foo Bar",
  389. "op": "register",
  390. },
  391. )
  392. assert resp.status_code == 200
  393. with self.feature("auth:register"), self.options({"auth.allow-registration": True}):
  394. resp = self.client.post(
  395. self.path,
  396. {
  397. "username": "test-a-really-long-email-address@example.com",
  398. "password": "foobar",
  399. "name": "Foo Bar",
  400. "op": "register",
  401. "subscribe": "0",
  402. },
  403. )
  404. assert resp.status_code == 302
  405. user = User.objects.get(username="test-a-really-long-email-address@example.com")
  406. assert user.email == "test-a-really-long-email-address@example.com"
  407. assert user.check_password("foobar")
  408. assert user.name == "Foo Bar"
  409. with assume_test_silo_mode(SiloMode.REGION):
  410. assert not OrganizationMember.objects.filter(user_id=user.id).exists()
  411. assert newsletter.backend.get_subscriptions(user) == {"subscriptions": []}
  412. def test_registration_subscribe_to_newsletter(self):
  413. with self.feature("auth:register"), self.options({"auth.allow-registration": True}):
  414. resp = self.client.post(
  415. self.path,
  416. {
  417. "username": "test-a-really-long-email-address@example.com",
  418. "password": "foobar",
  419. "name": "Foo Bar",
  420. "op": "register",
  421. "subscribe": "1",
  422. },
  423. )
  424. assert resp.status_code == 302
  425. user = User.objects.get(username="test-a-really-long-email-address@example.com")
  426. assert user.email == "test-a-really-long-email-address@example.com"
  427. assert user.check_password("foobar")
  428. assert user.name == "Foo Bar"
  429. results = newsletter.backend.get_subscriptions(user)["subscriptions"]
  430. assert len(results) == 1
  431. assert results[0].list_id == newsletter.backend.get_default_list_id()
  432. assert results[0].subscribed
  433. assert not results[0].verified
  434. @control_silo_test
  435. class AuthLoginCustomerDomainTest(TestCase):
  436. @cached_property
  437. def path(self):
  438. return reverse("sentry-login")
  439. def setUp(self):
  440. super().setUp()
  441. def disable_registration(self):
  442. return self.options({"auth.allow-registration": False})
  443. def test_renders_correct_template_existent_org(self):
  444. with self.disable_registration():
  445. resp = self.client.get(
  446. self.path,
  447. HTTP_HOST=f"{self.organization.slug}.testserver",
  448. follow=True,
  449. )
  450. assert resp.status_code == 200
  451. assert resp.redirect_chain == [("http://baz.testserver/auth/login/baz/", 302)]
  452. self.assertTemplateUsed("sentry/organization-login.html")
  453. def test_renders_correct_template_existent_org_preserve_querystring(self):
  454. with self.disable_registration():
  455. resp = self.client.get(
  456. f"{self.path}?one=two",
  457. HTTP_HOST=f"{self.organization.slug}.testserver",
  458. follow=True,
  459. )
  460. assert resp.status_code == 200
  461. assert resp.redirect_chain == [("http://baz.testserver/auth/login/baz/?one=two", 302)]
  462. self.assertTemplateUsed("sentry/organization-login.html")
  463. def test_renders_correct_template_nonexistent_org(self):
  464. with self.disable_registration():
  465. resp = self.client.get(
  466. self.path,
  467. HTTP_HOST="does-not-exist.testserver",
  468. )
  469. assert resp.status_code == 200
  470. self.assertTemplateUsed("sentry/login.html")
  471. def test_login_valid_credentials(self):
  472. # load it once for test cookie
  473. with self.disable_registration():
  474. self.client.get(self.path)
  475. resp = self.client.post(
  476. self.path,
  477. {"username": self.user.username, "password": "admin", "op": "login"},
  478. HTTP_HOST="albertos-apples.testserver",
  479. follow=True,
  480. )
  481. assert resp.status_code == 200
  482. assert resp.redirect_chain == [
  483. ("http://albertos-apples.testserver/auth/login/", 302),
  484. ("http://testserver/organizations/new/", 302),
  485. ]
  486. self.assertTemplateUsed("sentry/login.html")
  487. def test_login_valid_credentials_with_org(self):
  488. with self.disable_registration():
  489. self.create_organization(name="albertos-apples", owner=self.user)
  490. # load it once for test cookie
  491. self.client.get(self.path)
  492. resp = self.client.post(
  493. self.path,
  494. {"username": self.user.username, "password": "admin", "op": "login"},
  495. HTTP_HOST="albertos-apples.testserver",
  496. follow=True,
  497. )
  498. assert resp.status_code == 200
  499. assert resp.redirect_chain == [
  500. ("http://albertos-apples.testserver/auth/login/", 302),
  501. ("http://albertos-apples.testserver/issues/", 302),
  502. ]
  503. def test_login_valid_credentials_invalid_customer_domain(self):
  504. with self.feature("system:multi-region"), self.disable_registration():
  505. self.create_organization(name="albertos-apples", owner=self.user)
  506. # load it once for test cookie
  507. self.client.get(self.path)
  508. resp = self.client.post(
  509. self.path,
  510. {"username": self.user.username, "password": "admin", "op": "login"},
  511. HTTP_POST="invalid.testserver",
  512. follow=True,
  513. )
  514. assert resp.status_code == 200
  515. assert resp.redirect_chain == [
  516. ("http://albertos-apples.testserver/auth/login/", 302),
  517. ("http://albertos-apples.testserver/issues/", 302),
  518. ]
  519. def test_login_valid_credentials_non_staff(self):
  520. with self.disable_registration():
  521. org = self.create_organization(name="albertos-apples")
  522. non_staff_user = self.create_user(is_staff=False)
  523. self.create_member(organization=org, user=non_staff_user)
  524. # load it once for test cookie
  525. self.client.get(self.path)
  526. resp = self.client.post(
  527. self.path,
  528. {"username": non_staff_user.username, "password": "admin", "op": "login"},
  529. HTTP_HOST="albertos-apples.testserver",
  530. follow=True,
  531. )
  532. assert resp.status_code == 200
  533. assert resp.redirect_chain == [
  534. ("http://albertos-apples.testserver/auth/login/", 302),
  535. ("http://albertos-apples.testserver/issues/", 302),
  536. ]
  537. def test_login_valid_credentials_not_a_member(self):
  538. user = self.create_user()
  539. self.create_organization(name="albertos-apples")
  540. self.create_member(organization=self.organization, user=user)
  541. with self.disable_registration():
  542. # load it once for test cookie
  543. self.client.get(self.path)
  544. resp = self.client.post(
  545. self.path,
  546. {"username": user.username, "password": "admin", "op": "login"},
  547. HTTP_HOST="albertos-apples.testserver",
  548. follow=True,
  549. )
  550. assert resp.status_code == 200
  551. assert resp.redirect_chain == [
  552. (f"http://albertos-apples.testserver{reverse('sentry-login')}", 302),
  553. (
  554. f"http://albertos-apples.testserver{reverse('sentry-auth-organization', args=['albertos-apples'])}",
  555. 302,
  556. ),
  557. ]
  558. def test_login_valid_credentials_orgless(self):
  559. user = self.create_user()
  560. self.create_organization(name="albertos-apples")
  561. with self.disable_registration():
  562. # load it once for test cookie
  563. self.client.get(self.path)
  564. resp = self.client.post(
  565. self.path,
  566. {"username": user.username, "password": "admin", "op": "login"},
  567. HTTP_HOST="albertos-apples.testserver",
  568. follow=True,
  569. )
  570. assert resp.status_code == 200
  571. assert resp.redirect_chain == [
  572. ("http://albertos-apples.testserver/auth/login/", 302),
  573. ("http://albertos-apples.testserver/auth/login/albertos-apples/", 302),
  574. ]
  575. def test_login_valid_credentials_org_does_not_exist(self):
  576. user = self.create_user()
  577. with self.disable_registration():
  578. # load it once for test cookie
  579. self.client.get(self.path)
  580. resp = self.client.post(
  581. self.path,
  582. {"username": user.username, "password": "admin", "op": "login"},
  583. HTTP_HOST="albertos-apples.testserver",
  584. follow=True,
  585. )
  586. assert resp.status_code == 200
  587. assert resp.redirect_chain == [
  588. ("http://albertos-apples.testserver/auth/login/", 302),
  589. ("http://testserver/organizations/new/", 302),
  590. ]
  591. def test_login_redirects_to_sso_org_does_not_exist(self):
  592. # load it once for test cookie
  593. with self.disable_registration():
  594. user = self.create_user()
  595. self.client.get(self.path)
  596. user = self.create_user()
  597. resp = self.client.post(
  598. self.path,
  599. {
  600. "username": user.username,
  601. "password": "admin",
  602. "op": "sso",
  603. "organization": "foobar",
  604. },
  605. HTTP_HOST="albertos-apples.testserver",
  606. follow=True,
  607. )
  608. assert resp.status_code == 200
  609. assert resp.redirect_chain == [("/auth/login/", 302)] # Redirects to default login
  610. def test_login_redirects_to_sso_provider_does_not_exist(self):
  611. # load it once for test cookie
  612. with self.disable_registration():
  613. user = self.create_user()
  614. self.create_organization(name="albertos-apples")
  615. self.client.get(self.path)
  616. user = self.create_user()
  617. resp = self.client.post(
  618. self.path,
  619. {
  620. "username": user.username,
  621. "password": "admin",
  622. "op": "sso",
  623. "organization": "albertos-apples",
  624. },
  625. HTTP_HOST="albertos-apples.testserver",
  626. follow=True,
  627. )
  628. assert resp.status_code == 200
  629. assert resp.redirect_chain == [
  630. ("/auth/login/", 302),
  631. ("http://albertos-apples.testserver/auth/login/albertos-apples/", 302),
  632. ] # Redirects to default login
  633. def test_login_redirects_to_sso_provider(self):
  634. # load it once for test cookie
  635. with self.disable_registration():
  636. user = self.create_user()
  637. custom_organization = self.create_organization(name="albertos-apples")
  638. AuthProvider.objects.create(organization_id=custom_organization.id, provider="dummy")
  639. self.client.get(self.path)
  640. user = self.create_user()
  641. resp = self.client.post(
  642. self.path,
  643. {
  644. "username": user.username,
  645. "password": "admin",
  646. "op": "sso",
  647. "organization": "albertos-apples",
  648. },
  649. HTTP_HOST="albertos-apples.testserver",
  650. follow=True,
  651. )
  652. assert resp.status_code == 200
  653. assert resp.redirect_chain == [("/auth/login/albertos-apples/", 302)]