oncall-engine/engine/apps/api/tests/test_auth.py
Joey Orlando bfcc0b9f29
update URLs constructed by the backend to support IRM plugin (#5137)
# What this PR does

Introduces a new class,
`apps.grafana_plugin.ui_url_builder.UIURLBuilder`, which is responsible
for... building UI URLs (😄). The class mainly does two things:
- it will decide if the URL should point to `grafana-oncall-app` or
`grafana-irm-app` based on the value of
`organization.is_grafana_irm_enabled` (**NOTE**: this value isn't yet
being set + defaults to `False`; logic for setting this value will be
done in a subsequent PR)
- Adds `enum`s, `OnCallPage` and `IncidentPage` to DRYify hardcoded UI
URLs (in case we decide to change these slightly in the near future)

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
2024-10-09 08:55:10 -04:00

227 lines
8.3 KiB
Python

from unittest.mock import Mock, patch
import pytest
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponse
from django.test.utils import override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from apps.auth_token.constants import SLACK_AUTH_TOKEN_NAME
from apps.social_auth.backends import SLACK_INSTALLATION_BACKEND
from common.constants.plugin_ids import PluginID
from common.constants.slack_auth import SLACK_OAUTH_ACCESS_RESPONSE
GRAFANA_URL = "http://example.com"
@pytest.mark.django_db
@pytest.mark.parametrize(
"backend_name,expected_url",
(
("slack-login", f"{GRAFANA_URL}/a/{PluginID.ONCALL}/users/me"),
(SLACK_INSTALLATION_BACKEND, f"{GRAFANA_URL}/a/{PluginID.ONCALL}/chat-ops"),
),
)
def test_complete_slack_auth_redirect_ok(
make_organization,
make_user_for_organization,
make_slack_token_for_user,
backend_name,
expected_url,
):
organization = make_organization(grafana_url=GRAFANA_URL)
admin = make_user_for_organization(organization)
_, slack_token = make_slack_token_for_user(admin)
client = APIClient()
url = (
reverse("api-internal:complete-social-auth", kwargs={"backend": backend_name})
+ f"?{SLACK_AUTH_TOKEN_NAME}={slack_token}"
)
with patch("apps.api.views.auth.do_complete") as mock_do_complete:
mock_do_complete.return_value = None
response = client.get(url)
assert response.status_code == status.HTTP_302_FOUND
assert response.url == expected_url
@pytest.mark.django_db
def test_complete_slack_auth_redirect_error(
make_organization,
make_user_for_organization,
make_slack_token_for_user,
):
organization = make_organization()
admin = make_user_for_organization(organization)
_, slack_token = make_slack_token_for_user(admin)
client = APIClient()
url = (
reverse("api-internal:complete-social-auth", kwargs={"backend": "slack-login"})
+ f"?{SLACK_AUTH_TOKEN_NAME}={slack_token}"
)
def _custom_do_complete(backend, *args, **kwargs):
backend.strategy.session[REDIRECT_FIELD_NAME] = "some-url"
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
with patch("apps.api.views.auth.do_complete", side_effect=_custom_do_complete):
response = client.get(url)
assert response.status_code == status.HTTP_302_FOUND
assert response.url == "some-url"
@override_settings(UNIFIED_SLACK_APP_ENABLED=False)
@pytest.mark.django_db
def test_start_slack_ok(
make_organization_and_user_with_plugin_token,
make_user_auth_headers,
):
"""
Covers the case when user starts Slack integration installation via Grafana OnCall
"""
_, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
url = reverse("api-internal:social-auth", kwargs={"backend": SLACK_INSTALLATION_BACKEND})
mock_do_auth_return = Mock()
mock_do_auth_return.url = "https://slack_oauth_redirect.com"
with patch("apps.api.views.auth.do_auth", return_value=mock_do_auth_return) as mock_do_auth:
response = client.get(url, **make_user_auth_headers(user, token))
assert mock_do_auth.called
assert response.status_code == status.HTTP_200_OK
assert response.json() == "https://slack_oauth_redirect.com"
@override_settings(UNIFIED_SLACK_APP_ENABLED=True)
@pytest.mark.django_db
def test_start_unified_slack_ok(
make_organization_and_user_with_plugin_token,
make_user_auth_headers,
):
"""
Covers the case when user starts Unified Slack integration installation
"""
_, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
url = reverse("api-internal:social-auth", kwargs={"backend": SLACK_INSTALLATION_BACKEND})
with patch(
"apps.api.views.auth.get_installation_link_from_chatops_proxy", return_value="https://slack_oauth_redirect.com"
):
response = client.get(url, **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
assert response.json() == "https://slack_oauth_redirect.com"
@override_settings(UNIFIED_SLACK_APP_ENABLED=True)
@patch("apps.api.views.auth.get_installation_link_from_chatops_proxy", return_value=None)
@patch("apps.api.views.auth.get_slack_oauth_response_from_chatops_proxy", return_value=SLACK_OAUTH_ACCESS_RESPONSE)
@patch("apps.api.views.auth.install_slack_integration", return_value=None)
@pytest.mark.django_db
def test_start_slack_ok_via_chatops_proxy_when_already_installed(
mock_install_slack_integration,
mock_get_slack_oauth_response_from_chatops_proxy,
mock_get_installation_link_from_chatops_proxy,
make_organization_and_user_with_plugin_token,
make_user_auth_headers,
):
"""
Covers the case when user starts Unified Slack integration installation, but it's already installed.
It might happen if integration was installed from Incident side, but OnCall missed the corresponding event
"""
org, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
url = reverse("api-internal:social-auth", kwargs={"backend": SLACK_INSTALLATION_BACKEND})
response = client.get(url, **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_201_CREATED
assert mock_install_slack_integration.call_args.args == (org, user, SLACK_OAUTH_ACCESS_RESPONSE)
@pytest.mark.django_db
@patch("apps.social_auth.backends.GoogleOAuth2.get_redirect_uri")
@patch("apps.social_auth.backends.GoogleOAuth2Token.create_auth_token", return_value=("something", "token_string"))
@override_settings(SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE=["https://www.googleapis.com/auth/calendar.events.readonly"])
@override_settings(SOCIAL_AUTH_GOOGLE_OAUTH2_KEY="ouath2_key")
def test_google_start_auth_redirect_ok(
_mock_create_google_oauth2_auth_token,
mock_google_oauth2_backend_get_redirect_uri,
make_organization_and_user_with_plugin_token,
make_user_auth_headers,
):
redirect_uri = "http://testserver"
mock_google_oauth2_backend_get_redirect_uri.return_value = redirect_uri
_, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
url = reverse("api-internal:social-auth", kwargs={"backend": "google-oauth2"})
response = client.get(url, **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
assert response.json() == (
"https://accounts.google.com/o/oauth2/auth?client_id=ouath2_key"
f"&redirect_uri={redirect_uri}&response_type=code"
"&state=token_string&scope=https://www.googleapis.com/auth/calendar.events.readonly+openid+email+profile"
"&access_type=offline&approval_prompt=auto"
)
@pytest.mark.django_db
@patch("apps.api.views.auth.do_complete", return_value=None)
def test_google_complete_auth_redirect_ok(
_mock_do_complete,
make_organization,
make_user_for_organization,
make_google_oauth2_token_for_user,
):
organization = make_organization(grafana_url=GRAFANA_URL)
admin = make_user_for_organization(organization)
_, google_oauth2_token = make_google_oauth2_token_for_user(admin)
client = APIClient()
url = (
reverse("api-internal:complete-social-auth", kwargs={"backend": "google-oauth2"})
+ f"?state={google_oauth2_token}"
)
response = client.get(url)
assert response.status_code == status.HTTP_302_FOUND
assert response.url == f"{GRAFANA_URL}/a/{PluginID.ONCALL}/users/me"
@pytest.mark.django_db
def test_complete_google_auth_redirect_error(
make_organization,
make_user_for_organization,
make_google_oauth2_token_for_user,
):
organization = make_organization()
admin = make_user_for_organization(organization)
_, google_oauth2_token = make_google_oauth2_token_for_user(admin)
client = APIClient()
url = (
reverse("api-internal:complete-social-auth", kwargs={"backend": "google-oauth2"})
+ f"?state={google_oauth2_token}"
)
def _custom_do_complete(backend, *args, **kwargs):
backend.strategy.session[REDIRECT_FIELD_NAME] = "some-url"
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
with patch("apps.api.views.auth.do_complete", side_effect=_custom_do_complete):
response = client.get(url)
assert response.status_code == status.HTTP_302_FOUND
assert response.url == "some-url"