test_api.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. from urllib.parse import unquote
  2. from allauth.mfa.models import Authenticator
  3. from django.core import mail
  4. from django.test import TestCase, override_settings
  5. from django.urls import reverse
  6. from model_bakery import baker
  7. from apps.organizations_ext.models import OrganizationUserRole
  8. from apps.projects.models import UserProjectAlert
  9. from glitchtip.test_utils.test_case import GlitchTestCase
  10. from ..models import User
  11. class UserRegistrationTestCase(TestCase):
  12. def test_create_user(self):
  13. url = "/_allauth/browser/v1/auth/signup"
  14. data = {
  15. "email": "test@example.com",
  16. "password": "hunter222",
  17. }
  18. res = self.client.post(url, data, content_type="application/json")
  19. self.assertEqual(res.status_code, 200)
  20. def test_closed_registration(self):
  21. """Only first user may register"""
  22. url = "/_allauth/browser/v1/auth/signup"
  23. user1_data = {
  24. "email": "test1@example.com",
  25. "password": "hunter222",
  26. }
  27. user2_data = {
  28. "email": "test2@example.com",
  29. "password": "hunter222",
  30. }
  31. with override_settings(ENABLE_USER_REGISTRATION=False):
  32. res = self.client.post(url, user1_data, content_type="application/json")
  33. self.assertEqual(res.status_code, 200)
  34. res = self.client.post(url, user2_data, content_type="application/json")
  35. self.assertEqual(res.status_code, 409)
  36. class UsersTestCase(GlitchTestCase):
  37. @classmethod
  38. def setUpTestData(cls):
  39. cls.create_user()
  40. def setUp(self):
  41. self.client.force_login(self.user)
  42. def test_list(self):
  43. url = reverse("api:list_users")
  44. res = self.client.get(url)
  45. self.assertContains(res, self.user.email)
  46. def test_retrieve(self):
  47. url = reverse("api:get_user", args=["me"])
  48. res = self.client.get(url)
  49. self.assertContains(res, self.user.email)
  50. url = reverse("api:get_user", args=[self.user.id])
  51. res = self.client.get(url)
  52. self.assertContains(res, self.user.email)
  53. def test_destroy(self):
  54. other_user = baker.make("users.user")
  55. url = reverse("api:delete_user", args=[other_user.pk])
  56. res = self.client.delete(url)
  57. self.assertEqual(
  58. res.status_code, 404, "User should not be able to delete other users"
  59. )
  60. url = reverse("api:delete_user", args=[self.user.pk])
  61. res = self.client.delete(url)
  62. self.assertEqual(
  63. res.status_code, 400, "Not allowed to destroy owned organization"
  64. )
  65. # Delete organization to allow user deletion
  66. self.organization.delete()
  67. res = self.client.delete(url)
  68. self.assertEqual(res.status_code, 204)
  69. self.assertFalse(User.objects.filter(pk=self.user.pk).exists())
  70. def test_update(self):
  71. url = reverse("api:update_user", args=["me"])
  72. data = {"name": "new", "options": {"foo": "bar"}}
  73. res = self.client.put(url, data, content_type="application/json")
  74. self.assertContains(res, data["name"])
  75. self.assertContains(res, data["options"]["foo"])
  76. self.assertTrue(User.objects.filter(name=data["name"]).exists())
  77. def test_organization_members_list(self):
  78. other_user = baker.make("users.user")
  79. other_organization = baker.make("organizations_ext.Organization")
  80. other_organization.add_user(other_user, OrganizationUserRole.ADMIN)
  81. user2 = baker.make("users.User")
  82. self.organization.add_user(user2, OrganizationUserRole.MEMBER)
  83. url = reverse("api:list_organization_members", args=[self.organization.slug])
  84. res = self.client.get(url)
  85. self.assertContains(res, user2.email)
  86. self.assertNotContains(res, other_user.email)
  87. # Can't view members of groups you don't belong to
  88. url = reverse("api:list_organization_members", args=[other_organization.slug])
  89. res = self.client.get(url)
  90. self.assertNotContains(res, other_user.email)
  91. def test_emails_list(self):
  92. email_address = baker.make("account.EmailAddress", user=self.user)
  93. another_user = baker.make("users.user")
  94. another_email_address = baker.make("account.EmailAddress", user=another_user)
  95. url = reverse("api:list_emails", args=["me"])
  96. res = self.client.get(url)
  97. self.assertContains(res, email_address.email)
  98. self.assertNotContains(res, another_email_address.email)
  99. def test_emails_create(self):
  100. url = reverse("api:list_emails", args=["me"])
  101. res = self.client.post(
  102. url, {"email": "invalid"}, content_type="application/json"
  103. )
  104. self.assertEqual(res.status_code, 422)
  105. new_email = "new@exmaple.com"
  106. data = {"email": new_email}
  107. res = self.client.post(url, data, content_type="application/json")
  108. self.assertContains(res, new_email, status_code=201)
  109. self.assertTrue(
  110. self.user.emailaddress_set.filter(email=new_email, verified=False).exists()
  111. )
  112. self.assertEqual(len(mail.outbox), 1)
  113. # Ensure token is valid and can verify email
  114. body = mail.outbox[0].body
  115. key = unquote(body[body.find("confirm-email") :].split("/")[1])
  116. url = "/_allauth/browser/v1/auth/email/verify"
  117. data = {"key": key}
  118. res = self.client.post(url, data, content_type="application/json")
  119. self.assertTrue(
  120. self.user.emailaddress_set.filter(email=new_email, verified=True).exists()
  121. )
  122. def test_emails_create_dupe_email(self):
  123. url = reverse("api:create_email", args=["me"])
  124. email_address = baker.make(
  125. "account.EmailAddress",
  126. user=self.user,
  127. email="something@example.com",
  128. )
  129. data = {"email": email_address.email}
  130. res = self.client.post(url, data, content_type="application/json")
  131. self.assertContains(res, "already exists", status_code=400)
  132. def test_emails_create_dupe_email_other_user(self):
  133. url = reverse("api:create_email", args=["me"])
  134. email_address = baker.make(
  135. "account.EmailAddress", email="a@example.com", verified=True
  136. )
  137. data = {"email": email_address.email}
  138. res = self.client.post(url, data, content_type="application/json")
  139. self.assertContains(res, "already exists", status_code=400)
  140. def test_emails_set_primary(self):
  141. url = reverse("api:set_email_as_primary", args=["me"])
  142. email_address = baker.make(
  143. "account.EmailAddress", verified=True, user=self.user
  144. )
  145. data = {"email": email_address.email}
  146. res = self.client.put(url, data, content_type="application/json")
  147. self.assertContains(res, email_address.email, status_code=200)
  148. self.assertTrue(
  149. self.user.emailaddress_set.filter(
  150. email=email_address.email, primary=True
  151. ).exists()
  152. )
  153. extra_email = baker.make("account.EmailAddress", verified=True, user=self.user)
  154. data = {"email": extra_email.email}
  155. res = self.client.put(url, data)
  156. self.assertEqual(self.user.emailaddress_set.filter(primary=True).count(), 1)
  157. def test_emails_set_primary_unverified_primary(self):
  158. """
  159. Because confirmation is optional, it's possible to have an existing email that is primary and unverified
  160. """
  161. url = reverse("api:set_email_as_primary", args=["me"])
  162. email = "test@example.com"
  163. baker.make(
  164. "account.EmailAddress",
  165. primary=True,
  166. user=self.user,
  167. )
  168. baker.make(
  169. "account.EmailAddress",
  170. email=email,
  171. verified=True,
  172. user=self.user,
  173. )
  174. data = {"email": email}
  175. res = self.client.put(url, data, content_type="application/json")
  176. self.assertEqual(res.status_code, 200)
  177. def test_emails_destroy(self):
  178. url = reverse("api:delete_email", args=["me"])
  179. email_address = baker.make(
  180. "account.EmailAddress", verified=True, primary=False, user=self.user
  181. )
  182. data = {"email": email_address.email}
  183. res = self.client.delete(url, data, content_type="application/json")
  184. self.assertEqual(res.status_code, 204)
  185. self.assertFalse(
  186. self.user.emailaddress_set.filter(email=email_address.email).exists()
  187. )
  188. def test_emails_confirm(self):
  189. email_address = baker.make("account.EmailAddress", user=self.user)
  190. url = reverse("api:send_confirm_email", args=["me"])
  191. data = {"email": email_address.email}
  192. res = self.client.post(url, data, content_type="application/json")
  193. self.assertEqual(res.status_code, 204)
  194. self.assertEqual(len(mail.outbox), 1)
  195. def test_notifications_retrieve(self):
  196. url = reverse("api:get_notifications", args=["me"])
  197. res = self.client.get(url)
  198. self.assertContains(res, "subscribeByDefault")
  199. def test_notifications_update(self):
  200. url = reverse("api:update_notifications", args=["me"])
  201. data = {"subscribeByDefault": False}
  202. res = self.client.put(url, data, content_type="application/json")
  203. self.assertFalse(res.json().get("subscribeByDefault"))
  204. self.user.refresh_from_db()
  205. self.assertFalse(self.user.subscribe_by_default)
  206. def test_alerts_retrieve(self):
  207. url = reverse("api:user_notification_alerts", args=["me"])
  208. alert = baker.make(
  209. "projects.UserProjectAlert", user=self.user, project=self.project
  210. )
  211. res = self.client.get(url)
  212. self.assertContains(res, self.project.id)
  213. self.assertEqual(res.json()[str(self.project.id)], alert.status)
  214. def test_alerts_update(self):
  215. url = reverse("api:update_user_notification_alerts", args=["me"])
  216. # Set to alert to On
  217. data = {str(self.project.id): 1}
  218. res = self.client.put(url, data, content_type="application/json")
  219. self.assertEqual(res.status_code, 204)
  220. self.assertEqual(UserProjectAlert.objects.all().count(), 1)
  221. self.assertEqual(UserProjectAlert.objects.first().status, 1)
  222. # Set to alert to Off
  223. data = '{"' + str(self.project.id) + '":0}'
  224. res = self.client.put(url, data, content_type="application/json")
  225. self.assertEqual(res.status_code, 204)
  226. self.assertEqual(UserProjectAlert.objects.first().status, 0)
  227. # Set to alert to "default"
  228. data = '{"' + str(self.project.id) + '":-1}'
  229. res = self.client.put(url, data, content_type="application/json")
  230. self.assertEqual(res.status_code, 204)
  231. # Default deletes the row
  232. self.assertEqual(UserProjectAlert.objects.all().count(), 0)
  233. def test_reset_password(self):
  234. """
  235. Social accounts weren't getting reset password emails. This
  236. approximates the issue by testing an account that has an
  237. unusable password.
  238. """
  239. url = "/_allauth/browser/v1/auth/password/request"
  240. # Normal behavior
  241. self.client.post(
  242. url, {"email": self.user.email}, content_type="application/json"
  243. )
  244. self.assertEqual(len(mail.outbox), 1)
  245. user_without_password = baker.make("users.User")
  246. user_without_password.set_unusable_password()
  247. user_without_password.save()
  248. self.assertFalse(user_without_password.has_usable_password())
  249. self.client.post(
  250. url, {"email": user_without_password.email}, content_type="application/json"
  251. )
  252. self.assertEqual(len(mail.outbox), 2)
  253. def test_generate_recovery_codes(self):
  254. url = reverse("api:generate_recovery_codes")
  255. res = self.client.get(url)
  256. self.assertContains(res, "codes")
  257. code = res.json()["codes"][0]
  258. res = self.client.post(url, {"code": "0"}, content_type="application/json")
  259. self.assertEqual(res.status_code, 400)
  260. res = self.client.post(
  261. url,
  262. {"code": code},
  263. content_type="application/json",
  264. )
  265. self.assertEqual(res.status_code, 204)
  266. self.assertTrue(Authenticator.objects.filter(user=self.user).exists())