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.

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