diff --git a/engine/apps/api/tests/test_organization.py b/engine/apps/api/tests/test_organization.py index 4d04d6c1e..56d4b5ad6 100644 --- a/engine/apps/api/tests/test_organization.py +++ b/engine/apps/api/tests/test_organization.py @@ -218,6 +218,30 @@ def test_organization_get_channel_verification_code_ok( mock_generate_code.assert_called_once_with(organization) +@pytest.mark.django_db +@pytest.mark.parametrize( + "role,expected_status", + [ + (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), + (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), + (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), + (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), + ], +) +def test_organization_get_mattermost_setup_details( + make_organization_and_user_with_plugin_token, + make_user_auth_headers, + role, + expected_status, +): + _, tester, token = make_organization_and_user_with_plugin_token(role) + client = APIClient() + url = reverse("api-internal:api-get-mattermost-setup-details") + response = client.get(url, format="json", **make_user_auth_headers(tester, token)) + + assert response.status_code == expected_status + + @pytest.mark.django_db def test_organization_get_channel_verification_code_invalid( make_organization_and_user_with_plugin_token, diff --git a/engine/apps/api/views/organization.py b/engine/apps/api/views/organization.py index 4c7f85542..733c0cc5d 100644 --- a/engine/apps/api/views/organization.py +++ b/engine/apps/api/views/organization.py @@ -117,6 +117,9 @@ class GetMattermostSetupDetails(APIView): "get": [RBACPermission.Permissions.INTEGRATIONS_WRITE], } + def _create_engine_url(self, auth_token) -> str: + return f"{settings.BASE_URL}/mattermost/manifest?auth_token={auth_token}" + def get(self, request): organization = request.auth.organization user = request.user @@ -126,7 +129,7 @@ def get(self, request): existing_auth_token = organization.mattermost_auth_token existing_auth_token.delete() _, auth_token = MattermostAuthToken.create_auth_token(user=user, organization=organization) - manifest_link = f"{settings.BASE_URL}/mattermost/manifest?auth_token={auth_token}" + manifest_link = self._create_engine_url(auth_token=auth_token) return Response({"manifest_link": manifest_link}) diff --git a/engine/apps/mattermost/tests/__init__.py b/engine/apps/mattermost/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/engine/apps/mattermost/tests/test_api_endpoint.py b/engine/apps/mattermost/tests/test_api_endpoint.py new file mode 100644 index 000000000..7eeef3645 --- /dev/null +++ b/engine/apps/mattermost/tests/test_api_endpoint.py @@ -0,0 +1,97 @@ +import pytest +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APIClient + + +def _create_callback_request_body(token): + return { "context":{ "app": { "app_id": "oncall-app-id" } }, "state": { "auth_token": token } } + +def _create_manifest_data(auth_token, base_url): + return { + "app_id": "Grafana-OnCall", + "version": "1.0.0", + "display_name": "Grafana OnCall", + "description": "Grafana OnCall app for sending and receiving events from mattermost", + "homepage_url": "https://grafana.com/docs/oncall/latest/", + "requested_permissions": [ "act_as_bot" ], + "requested_locations": [ "/in_post", "/post_menu", "/command" ], + "on_install": { + "path": "/mattermost/install", + "expand": { "app": "summary", "acting_user": "summary" }, + "state": { "auth_token": auth_token } + }, + "bindings": { "path": "/mattermost/bindings", "state": { "auth_token": auth_token } }, + "http": { "root_url": base_url } + } + +@pytest.mark.django_db +def test_get_manifest_data_success( + settings, + make_organization_and_user, + make_mattermost_app_verification_token_for_user +): + organization, user = make_organization_and_user() + _, token = make_mattermost_app_verification_token_for_user(user, organization) + url = reverse("mattermost:manifest") + client = APIClient() + response = client.get(url + f"?auth_token={token}") + assert response.status_code == status.HTTP_200_OK + + expected_manifest_data = _create_manifest_data(token, settings.BASE_URL) + assert response.json() == expected_manifest_data + +@pytest.mark.django_db +def test_get_manifest_data_forbidden( + make_organization_and_user, + make_mattermost_app_verification_token_for_user +): + organization, user = make_organization_and_user() + _, _ = make_mattermost_app_verification_token_for_user(user, organization) + url = reverse("mattermost:manifest") + client = APIClient() + response = client.get(url + f"?auth_token=wrongtoken") + assert response.status_code == status.HTTP_403_FORBIDDEN + +@pytest.mark.django_db +def test_install_callback_success( + make_organization_and_user, + make_mattermost_app_verification_token_for_user +): + organization, user = make_organization_and_user() + _, token = make_mattermost_app_verification_token_for_user(user, organization) + url = reverse("mattermost:install") + client = APIClient() + data = _create_callback_request_body(token) + response = client.post(url, data, format="json") + assert response.status_code == status.HTTP_200_OK + assert response.data["type"] == "ok" + +@pytest.mark.django_db +def test_bindings_callback_success( + make_organization_and_user, + make_mattermost_app_verification_token_for_user +): + organization, user = make_organization_and_user() + _, token = make_mattermost_app_verification_token_for_user(user, organization) + url = reverse("mattermost:bindings") + client = APIClient() + data = _create_callback_request_body(token) + response = client.post(url, data, format="json") + assert response.status_code == status.HTTP_200_OK + assert response.data["type"] == "ok" + +@pytest.mark.django_db +@pytest.mark.parametrize("path",["install", "bindings"]) +def test_install_callback_forbiden( + make_organization_and_user, + make_mattermost_app_verification_token_for_user, + path +): + organization, user = make_organization_and_user() + _, _ = make_mattermost_app_verification_token_for_user(user, organization) + url = reverse(f"mattermost:{path}") + client = APIClient() + data = _create_callback_request_body("wrongtoken") + response = client.post(url, data, format="json") + assert response.status_code == status.HTTP_403_FORBIDDEN \ No newline at end of file diff --git a/engine/apps/mattermost/urls.py b/engine/apps/mattermost/urls.py index 44a75a8f9..911c3399e 100644 --- a/engine/apps/mattermost/urls.py +++ b/engine/apps/mattermost/urls.py @@ -2,8 +2,10 @@ from .views import GetMattermostManifest, MattermostBindings, MattermostInstall +app_name = "mattermost" + urlpatterns = [ - path("manifest", GetMattermostManifest.as_view()), - path("install", MattermostInstall.as_view()), - path("bindings", MattermostBindings.as_view()), + path("manifest", GetMattermostManifest.as_view(), name="manifest"), + path("install", MattermostInstall.as_view(), name="install"), + path("bindings", MattermostBindings.as_view(), name="bindings"), ] diff --git a/engine/apps/mattermost/views.py b/engine/apps/mattermost/views.py index f0fa04f1e..212653012 100644 --- a/engine/apps/mattermost/views.py +++ b/engine/apps/mattermost/views.py @@ -1,3 +1,4 @@ +from django.conf import settings from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView @@ -29,16 +30,16 @@ def _build_bindings_callback(self, auth_token: str) -> dict: def _build_manifest(self, auth_token: str) -> dict: return { - "app_id": "Grafana-Oncall", + "app_id": "Grafana-OnCall", "version": "1.0.0", - "display_name": "Grafana Oncall", - "description": "Grafana Oncall app for sending and receiving events from mattermost", + "display_name": "Grafana OnCall", + "description": "Grafana OnCall app for sending and receiving events from mattermost", "homepage_url": "https://grafana.com/docs/oncall/latest/", "requested_permissions": ["act_as_bot"], "requested_locations": ["/in_post", "/post_menu", "/command"], "on_install": self._build_on_install_callback(auth_token=auth_token), "bindings": self._build_bindings_callback(auth_token=auth_token), - "http": {"root_url": "http://host.docker.internal:8080"}, + "http": {"root_url": settings.BASE_URL}, } diff --git a/engine/conftest.py b/engine/conftest.py index 899679fd2..ebc815d42 100644 --- a/engine/conftest.py +++ b/engine/conftest.py @@ -71,6 +71,7 @@ LabelValueFactory, WebhookAssociatedLabelFactory, ) +from apps.mattermost.models import MattermostAuthToken from apps.mobile_app.models import MobileAppAuthToken, MobileAppVerificationToken from apps.phone_notifications.phone_backend import PhoneBackend from apps.phone_notifications.tests.factories import PhoneCallRecordFactory, SMSRecordFactory @@ -280,6 +281,14 @@ def _make_mobile_app_auth_token_for_user(user, organization): return _make_mobile_app_auth_token_for_user +@pytest.fixture +def make_mattermost_app_verification_token_for_user(): + def _make_mattermost_app_verification_token_for_user(user, organization): + return MattermostAuthToken.create_auth_token(user, organization) + + return _make_mattermost_app_verification_token_for_user + + @pytest.fixture def make_slack_token_for_user(): def _make_slack_token_for_user(user):