test_api.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. from datetime import datetime, timedelta, timezone
  2. from django.urls import reverse
  3. from sentry.models import AuthIdentity, AuthProvider
  4. from sentry.testutils import AuthProviderTestCase
  5. from sentry.utils.auth import SSO_EXPIRY_TIME, SsoSession
  6. from sentry.utils.linksign import generate_signed_link
  7. # TODO: move these into the tests/sentry/auth directory and remove deprecated logic
  8. class AuthenticationTest(AuthProviderTestCase):
  9. def setUp(self):
  10. self.organization = self.create_organization(name="foo")
  11. self.user = self.create_user("foobar@example.com", is_superuser=False)
  12. team = self.create_team(name="bar", organization=self.organization)
  13. self.project = self.create_project(name="baz", organization=self.organization, teams=[team])
  14. member = self.create_member(user=self.user, organization=self.organization, teams=[team])
  15. setattr(member.flags, "sso:linked", True)
  16. member.save()
  17. event = self.store_event(data={}, project_id=self.project.id)
  18. group_id = event.group_id
  19. auth_provider = AuthProvider.objects.create(
  20. organization=self.organization, provider="dummy", flags=0
  21. )
  22. AuthIdentity.objects.create(auth_provider=auth_provider, user=self.user)
  23. self.login_as(self.user)
  24. self.paths = (
  25. f"/api/0/organizations/{self.organization.slug}/",
  26. f"/api/0/projects/{self.organization.slug}/{self.project.slug}/",
  27. f"/api/0/teams/{self.organization.slug}/{self.team.slug}/",
  28. f"/api/0/issues/{group_id}/",
  29. # this uses the internal API, which once upon a time was broken
  30. f"/api/0/issues/{group_id}/events/latest/",
  31. )
  32. def test_sso_auth_required(self):
  33. # we should be redirecting the user to the authentication form as they
  34. # haven't verified this specific organization
  35. self._test_paths_with_status(401)
  36. def test_sso_superuser_required(self):
  37. # superuser should still require SSO as they're a member of the org
  38. self.user.update(is_superuser=True)
  39. self._test_paths_with_status(401)
  40. def test_sso_with_expiry_valid(self):
  41. sso_session = SsoSession.create(self.organization.id)
  42. self.session[sso_session.session_key] = sso_session.to_dict()
  43. self.save_session()
  44. self._test_paths_with_status(200)
  45. def test_sso_with_expiry_expired(self):
  46. sso_session_expired = SsoSession(
  47. self.organization.id,
  48. datetime.now(tz=timezone.utc) - SSO_EXPIRY_TIME - timedelta(hours=1),
  49. )
  50. self.session[sso_session_expired.session_key] = sso_session_expired.to_dict()
  51. self.save_session()
  52. self._test_paths_with_status(401)
  53. def test_sso_redirect_url_internal(self):
  54. sso_session_expired = SsoSession(
  55. self.organization.id,
  56. datetime.now(tz=timezone.utc) - SSO_EXPIRY_TIME - timedelta(hours=1),
  57. )
  58. self.session[sso_session_expired.session_key] = sso_session_expired.to_dict()
  59. self.save_session()
  60. resp = self.client.get(
  61. f"/api/0/teams/{self.organization.slug}/{self.team.slug}/",
  62. HTTP_REFERER=f"/organizations/{self.organization.slug}/teams",
  63. )
  64. assert (
  65. resp.data["detail"]["extra"]["loginUrl"]
  66. == "/auth/login/foo/?next=%2Forganizations%2Ffoo%2Fteams"
  67. )
  68. def test_sso_redirect_url_internal_with_domain(self):
  69. sso_session_expired = SsoSession(
  70. self.organization.id,
  71. datetime.now(tz=timezone.utc) - SSO_EXPIRY_TIME - timedelta(hours=1),
  72. )
  73. self.session[sso_session_expired.session_key] = sso_session_expired.to_dict()
  74. self.save_session()
  75. resp = self.client.get(
  76. f"/api/0/teams/{self.organization.slug}/{self.team.slug}/",
  77. HTTP_REFERER=f"https://testdomain.com/organizations/{self.organization.slug}/teams",
  78. SERVER_NAME="testdomain.com",
  79. )
  80. assert (
  81. resp.data["detail"]["extra"]["loginUrl"]
  82. == "/auth/login/foo/?next=https%3A%2F%2Ftestdomain.com%2Forganizations%2Ffoo%2Fteams"
  83. )
  84. def test_sso_redirect_url_external_removed(self):
  85. sso_session_expired = SsoSession(
  86. self.organization.id,
  87. datetime.now(tz=timezone.utc) - SSO_EXPIRY_TIME - timedelta(hours=1),
  88. )
  89. self.session[sso_session_expired.session_key] = sso_session_expired.to_dict()
  90. self.save_session()
  91. resp = self.client.get(
  92. f"/api/0/teams/{self.organization.slug}/{self.team.slug}/",
  93. HTTP_REFERER="http://example.com",
  94. )
  95. assert resp.data["detail"]["extra"]["loginUrl"] == "/auth/login/foo/"
  96. def _test_paths_with_status(self, status):
  97. for path in self.paths:
  98. resp = self.client.get(path)
  99. assert resp.status_code == status, (resp.status_code, resp.content)
  100. def test_sso_auth_required_signed_link(self):
  101. unsigned_link = reverse(
  102. "sentry-api-0-project-fix-processing-issues",
  103. kwargs={"project_slug": self.project.slug, "organization_slug": self.organization.slug},
  104. )
  105. resp = self.client.get(unsigned_link)
  106. assert resp.status_code == 401, (resp.status_code, resp.content)
  107. signed_link = generate_signed_link(
  108. self.user,
  109. "sentry-api-0-project-fix-processing-issues",
  110. kwargs={"project_slug": self.project.slug, "organization_slug": self.organization.slug},
  111. )
  112. resp = self.client.get(signed_link)
  113. assert resp.status_code == 200