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.

352 lines
13 KiB

5 years ago
  1. from django.core import mail
  2. from django.conf import settings
  3. from django.test.utils import override_settings
  4. from hc.test import BaseTestCase
  5. from hc.accounts.models import Member, Project
  6. from hc.api.models import TokenBucket
  7. class ProjectTestCase(BaseTestCase):
  8. def setUp(self):
  9. super().setUp()
  10. self.url = "/projects/%s/settings/" % self.project.code
  11. def test_it_checks_access(self):
  12. self.client.login(username="[email protected]", password="password")
  13. r = self.client.get(self.url)
  14. self.assertEqual(r.status_code, 404)
  15. def test_it_allows_team_access(self):
  16. self.client.login(username="[email protected]", password="password")
  17. r = self.client.get(self.url)
  18. self.assertContains(r, "Change Project Name")
  19. def test_it_shows_api_keys(self):
  20. self.project.api_key_readonly = "R" * 32
  21. self.project.save()
  22. self.client.login(username="[email protected]", password="password")
  23. form = {"show_api_keys": "1"}
  24. r = self.client.post(self.url, form)
  25. self.assertEqual(r.status_code, 200)
  26. self.assertContains(r, "X" * 32)
  27. self.assertContains(r, "R" * 32)
  28. self.assertContains(r, "Prometheus metrics endpoint")
  29. def test_it_creates_api_key(self):
  30. self.client.login(username="[email protected]", password="password")
  31. form = {"create_api_keys": "1"}
  32. r = self.client.post(self.url, form)
  33. self.assertEqual(r.status_code, 200)
  34. self.project.refresh_from_db()
  35. api_key = self.project.api_key
  36. self.assertTrue(len(api_key) > 10)
  37. self.assertFalse("b'" in api_key)
  38. def test_it_requires_rw_access_to_create_api_key(self):
  39. self.bobs_membership.role = "r"
  40. self.bobs_membership.save()
  41. self.client.login(username="[email protected]", password="password")
  42. r = self.client.post(self.url, {"create_api_keys": "1"})
  43. self.assertEqual(r.status_code, 403)
  44. def test_it_revokes_api_key(self):
  45. self.project.api_key_readonly = "R" * 32
  46. self.project.save()
  47. self.client.login(username="[email protected]", password="password")
  48. r = self.client.post(self.url, {"revoke_api_keys": "1"})
  49. self.assertEqual(r.status_code, 200)
  50. self.project.refresh_from_db()
  51. self.assertEqual(self.project.api_key, "")
  52. self.assertEqual(self.project.api_key_readonly, "")
  53. def test_it_requires_rw_access_to_revoke_api_key(self):
  54. self.bobs_membership.role = "r"
  55. self.bobs_membership.save()
  56. self.client.login(username="[email protected]", password="password")
  57. r = self.client.post(self.url, {"revoke_api_keys": "1"})
  58. self.assertEqual(r.status_code, 403)
  59. def test_it_adds_team_member(self):
  60. self.client.login(username="[email protected]", password="password")
  61. form = {"invite_team_member": "1", "email": "[email protected]", "role": "w"}
  62. r = self.client.post(self.url, form)
  63. self.assertEqual(r.status_code, 200)
  64. members = self.project.member_set.all()
  65. self.assertEqual(members.count(), 2)
  66. member = Member.objects.get(
  67. project=self.project, user__email="[email protected]"
  68. )
  69. # The read-write flag should be set
  70. self.assertEqual(member.role, member.Role.REGULAR)
  71. # The new user should not have their own project
  72. self.assertFalse(member.user.project_set.exists())
  73. # And an email should have been sent
  74. subj = f"You have been invited to join Alices Project on {settings.SITE_NAME}"
  75. self.assertHTMLEqual(mail.outbox[0].subject, subj)
  76. def test_it_adds_readonly_team_member(self):
  77. self.client.login(username="[email protected]", password="password")
  78. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  79. r = self.client.post(self.url, form)
  80. self.assertEqual(r.status_code, 200)
  81. member = Member.objects.get(
  82. project=self.project, user__email="[email protected]"
  83. )
  84. self.assertEqual(member.role, member.Role.READONLY)
  85. def test_it_adds_manager_team_member(self):
  86. self.client.login(username="[email protected]", password="password")
  87. form = {"invite_team_member": "1", "email": "[email protected]", "role": "m"}
  88. r = self.client.post(self.url, form)
  89. self.assertEqual(r.status_code, 200)
  90. member = Member.objects.get(
  91. project=self.project, user__email="[email protected]"
  92. )
  93. # The new user should have role manager
  94. self.assertEqual(member.role, member.Role.MANAGER)
  95. def test_it_adds_member_from_another_team(self):
  96. # With team limit at zero, we should not be able to invite any new users
  97. self.profile.team_limit = 0
  98. self.profile.save()
  99. # But Charlie will have an existing membership in another Alice's project
  100. # so Alice *should* be able to invite Charlie:
  101. p2 = Project.objects.create(owner=self.alice)
  102. Member.objects.create(user=self.charlie, project=p2)
  103. self.client.login(username="[email protected]", password="password")
  104. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  105. r = self.client.post(self.url, form)
  106. self.assertEqual(r.status_code, 200)
  107. q = Member.objects.filter(project=self.project, user=self.charlie)
  108. self.assertEqual(q.count(), 1)
  109. # And this should not have affected the rate limit:
  110. q = TokenBucket.objects.filter(value="invite-%d" % self.alice.id)
  111. self.assertFalse(q.exists())
  112. def test_it_rejects_duplicate_membership(self):
  113. self.client.login(username="[email protected]", password="password")
  114. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  115. r = self.client.post(self.url, form)
  116. self.assertContains(r, "[email protected] is already a member")
  117. # The number of memberships should have not increased
  118. self.assertEqual(self.project.member_set.count(), 1)
  119. def test_it_rejects_owner_as_a_member(self):
  120. self.client.login(username="[email protected]", password="password")
  121. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  122. r = self.client.post(self.url, form)
  123. self.assertContains(r, "[email protected] is already a member")
  124. # The number of memberships should have not increased
  125. self.assertEqual(self.project.member_set.count(), 1)
  126. def test_it_rejects_too_long_email_addresses(self):
  127. self.client.login(username="[email protected]", password="password")
  128. aaa = "a" * 300
  129. form = {
  130. "invite_team_member": "1",
  131. "email": f"frank+{aaa}@example.org",
  132. "role": "r",
  133. }
  134. r = self.client.post(self.url, form)
  135. self.assertEqual(r.status_code, 200)
  136. # No email should have been sent
  137. self.assertEqual(len(mail.outbox), 0)
  138. @override_settings(SECRET_KEY="test-secret")
  139. def test_it_rate_limits_invites(self):
  140. obj = TokenBucket(value="invite-%d" % self.alice.id)
  141. obj.tokens = 0
  142. obj.save()
  143. self.client.login(username="[email protected]", password="password")
  144. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  145. r = self.client.post(self.url, form)
  146. self.assertContains(r, "Too Many Requests")
  147. self.assertEqual(len(mail.outbox), 0)
  148. def test_it_lets_manager_add_team_member(self):
  149. # Bob is a manager:
  150. self.bobs_membership.role = "m"
  151. self.bobs_membership.save()
  152. self.client.login(username="[email protected]", password="password")
  153. form = {"invite_team_member": "1", "email": "[email protected]", "role": "w"}
  154. r = self.client.post(self.url, form)
  155. self.assertEqual(r.status_code, 200)
  156. Member.objects.get(project=self.project, user__email="[email protected]")
  157. def test_it_does_not_allow_regular_member_invite_team_members(self):
  158. self.client.login(username="[email protected]", password="password")
  159. form = {"invite_team_member": "1", "email": "[email protected]", "role": "w"}
  160. r = self.client.post(self.url, form)
  161. self.assertEqual(r.status_code, 403)
  162. def test_it_checks_team_size(self):
  163. self.profile.team_limit = 0
  164. self.profile.save()
  165. self.client.login(username="[email protected]", password="password")
  166. form = {"invite_team_member": "1", "email": "[email protected]", "role": "r"}
  167. r = self.client.post(self.url, form)
  168. self.assertEqual(r.status_code, 403)
  169. def test_it_lets_owner_remove_team_member(self):
  170. self.client.login(username="[email protected]", password="password")
  171. form = {"remove_team_member": "1", "email": "[email protected]"}
  172. r = self.client.post(self.url, form)
  173. self.assertEqual(r.status_code, 200)
  174. self.assertFalse(Member.objects.exists())
  175. def test_it_lets_manager_remove_team_member(self):
  176. # Bob is a manager:
  177. self.bobs_membership.role = "m"
  178. self.bobs_membership.save()
  179. # Bob will try to remove this membership:
  180. Member.objects.create(user=self.charlie, project=self.project)
  181. self.client.login(username="[email protected]", password="password")
  182. form = {"remove_team_member": "1", "email": "[email protected]"}
  183. r = self.client.post(self.url, form)
  184. self.assertEqual(r.status_code, 200)
  185. q = Member.objects.filter(user=self.charlie, project=self.project)
  186. self.assertFalse(q.exists())
  187. def test_it_does_not_allow_regular_member_remove_team_member(self):
  188. # Bob will try to remove this membership:
  189. Member.objects.create(user=self.charlie, project=self.project)
  190. self.client.login(username="[email protected]", password="password")
  191. form = {"remove_team_member": "1", "email": "[email protected]"}
  192. r = self.client.post(self.url, form)
  193. self.assertEqual(r.status_code, 403)
  194. def test_it_rejects_manager_remove_self(self):
  195. self.bobs_membership.role = "m"
  196. self.bobs_membership.save()
  197. self.client.login(username="[email protected]", password="password")
  198. form = {"remove_team_member": "1", "email": "[email protected]"}
  199. r = self.client.post(self.url, form)
  200. self.assertEqual(r.status_code, 400)
  201. # The number of memberships should have not decreased
  202. self.assertEqual(self.project.member_set.count(), 1)
  203. def test_it_checks_membership_when_removing_team_member(self):
  204. self.client.login(username="[email protected]", password="password")
  205. url = "/projects/%s/settings/" % self.charlies_project.code
  206. form = {"remove_team_member": "1", "email": "[email protected]"}
  207. r = self.client.post(url, form)
  208. self.assertEqual(r.status_code, 400)
  209. def test_it_sets_project_name(self):
  210. self.client.login(username="[email protected]", password="password")
  211. form = {"set_project_name": "1", "name": "Alpha Team"}
  212. r = self.client.post(self.url, form)
  213. self.assertEqual(r.status_code, 200)
  214. self.project.refresh_from_db()
  215. self.assertEqual(self.project.name, "Alpha Team")
  216. def test_it_requires_rw_access_to_set_project_name(self):
  217. self.bobs_membership.role = "r"
  218. self.bobs_membership.save()
  219. self.client.login(username="[email protected]", password="password")
  220. form = {"set_project_name": "1", "name": "Alpha Team"}
  221. r = self.client.post(self.url, form)
  222. self.assertEqual(r.status_code, 403)
  223. def test_it_shows_invite_suggestions(self):
  224. p2 = Project.objects.create(owner=self.alice)
  225. self.client.login(username="[email protected]", password="password")
  226. r = self.client.get("/projects/%s/settings/" % p2.code)
  227. self.assertContains(r, "Add Users from Other Teams")
  228. self.assertContains(r, "[email protected]")
  229. def test_it_requires_rw_access_to_update_project_name(self):
  230. self.bobs_membership.role = "r"
  231. self.bobs_membership.save()
  232. self.client.login(username="[email protected]", password="password")
  233. form = {"set_project_name": "1", "name": "Alpha Team"}
  234. r = self.client.post(self.url, form)
  235. self.assertEqual(r.status_code, 403)
  236. def test_it_hides_actions_for_readonly_users(self):
  237. self.bobs_membership.role = "r"
  238. self.bobs_membership.save()
  239. self.client.login(username="[email protected]", password="password")
  240. r = self.client.get(self.url)
  241. self.assertNotContains(r, "#set-project-name-modal", status_code=200)
  242. self.assertNotContains(r, "Show API Keys")
  243. @override_settings(PROMETHEUS_ENABLED=False)
  244. def test_it_hides_prometheus_link_if_prometheus_not_enabled(self):
  245. self.project.api_key_readonly = "R" * 32
  246. self.project.save()
  247. self.client.login(username="[email protected]", password="password")
  248. r = self.client.post(self.url, {"show_api_keys": "1"})
  249. self.assertEqual(r.status_code, 200)
  250. self.assertNotContains(r, "Prometheus metrics endpoint")
  251. def test_it_requires_rw_access_to_show_api_key(self):
  252. self.bobs_membership.role = "r"
  253. self.bobs_membership.save()
  254. self.client.login(username="[email protected]", password="password")
  255. r = self.client.post(self.url, {"show_api_keys": "1"})
  256. self.assertEqual(r.status_code, 403)