forked from GithubBackups/healthchecks
Project code in URL for the "Add Discord" page. cc: #336
This commit is contained in:
parent
38bd84cc91
commit
f13ad875a1
@ -1,14 +1,12 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from hc.api.models import Channel
|
|
||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
from mock import patch
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(DISCORD_CLIENT_ID="t1", DISCORD_CLIENT_SECRET="s1")
|
@override_settings(DISCORD_CLIENT_ID="t1", DISCORD_CLIENT_SECRET="s1")
|
||||||
class AddDiscordTestCase(BaseTestCase):
|
class AddDiscordTestCase(BaseTestCase):
|
||||||
url = "/integrations/add_discord/"
|
def setUp(self):
|
||||||
|
super(AddDiscordTestCase, self).setUp()
|
||||||
|
self.url = "/projects/%s/add_discord/" % self.project.code
|
||||||
|
|
||||||
def test_instructions_work(self):
|
def test_instructions_work(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
@ -17,49 +15,10 @@ class AddDiscordTestCase(BaseTestCase):
|
|||||||
self.assertContains(r, "discordapp.com/api/oauth2/authorize")
|
self.assertContains(r, "discordapp.com/api/oauth2/authorize")
|
||||||
|
|
||||||
# There should now be a key in session
|
# There should now be a key in session
|
||||||
self.assertTrue("discord" in self.client.session)
|
self.assertTrue("add_discord" in self.client.session)
|
||||||
|
|
||||||
@override_settings(DISCORD_CLIENT_ID=None)
|
@override_settings(DISCORD_CLIENT_ID=None)
|
||||||
def test_it_requires_client_id(self):
|
def test_it_requires_client_id(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.get(self.url)
|
r = self.client.get(self.url)
|
||||||
self.assertEqual(r.status_code, 404)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
@patch("hc.front.views.requests.post")
|
|
||||||
def test_it_handles_oauth_response(self, mock_post):
|
|
||||||
session = self.client.session
|
|
||||||
session["discord"] = "foo"
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
oauth_response = {
|
|
||||||
"access_token": "test-token",
|
|
||||||
"webhook": {"url": "foo", "id": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
mock_post.return_value.text = json.dumps(oauth_response)
|
|
||||||
mock_post.return_value.json.return_value = oauth_response
|
|
||||||
|
|
||||||
url = self.url + "?code=12345678&state=foo"
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(url, follow=True)
|
|
||||||
self.assertRedirects(r, "/integrations/")
|
|
||||||
self.assertContains(r, "The Discord integration has been added!")
|
|
||||||
|
|
||||||
ch = Channel.objects.get()
|
|
||||||
self.assertEqual(ch.discord_webhook_url, "foo")
|
|
||||||
self.assertEqual(ch.project, self.project)
|
|
||||||
|
|
||||||
# Session should now be clean
|
|
||||||
self.assertFalse("discord" in self.client.session)
|
|
||||||
|
|
||||||
def test_it_avoids_csrf(self):
|
|
||||||
session = self.client.session
|
|
||||||
session["discord"] = "foo"
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
url = self.url + "?code=12345678&state=bar"
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(url)
|
|
||||||
self.assertEqual(r.status_code, 400)
|
|
||||||
|
70
hc/front/tests/test_add_discord_complete.py
Normal file
70
hc/front/tests/test_add_discord_complete.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from hc.api.models import Channel
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(DISCORD_CLIENT_ID="t1", DISCORD_CLIENT_SECRET="s1")
|
||||||
|
class AddDiscordCompleteTestCase(BaseTestCase):
|
||||||
|
url = "/integrations/add_discord/"
|
||||||
|
|
||||||
|
@patch("hc.front.views.requests.post")
|
||||||
|
def test_it_handles_oauth_response(self, mock_post):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_discord"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
oauth_response = {
|
||||||
|
"access_token": "test-token",
|
||||||
|
"webhook": {"url": "foo", "id": "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_post.return_value.text = json.dumps(oauth_response)
|
||||||
|
mock_post.return_value.json.return_value = oauth_response
|
||||||
|
|
||||||
|
url = self.url + "?code=12345678&state=foo"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url, follow=True)
|
||||||
|
self.assertRedirects(r, self.channels_url)
|
||||||
|
self.assertContains(r, "The Discord integration has been added!")
|
||||||
|
|
||||||
|
ch = Channel.objects.get()
|
||||||
|
self.assertEqual(ch.discord_webhook_url, "foo")
|
||||||
|
self.assertEqual(ch.project, self.project)
|
||||||
|
|
||||||
|
# Session should now be clean
|
||||||
|
self.assertFalse("add_discord" in self.client.session)
|
||||||
|
|
||||||
|
def test_it_avoids_csrf(self):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_discord"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
url = self.url + "?code=12345678&state=bar"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
|
# Session should now be clean
|
||||||
|
self.assertFalse("add_discord" in self.client.session)
|
||||||
|
|
||||||
|
def test_it_handles_access_denied(self):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_discord"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
url = self.url + "?error=access_denied"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url, follow=True)
|
||||||
|
self.assertRedirects(r, self.channels_url)
|
||||||
|
self.assertContains(r, "Discord setup was cancelled.")
|
||||||
|
|
||||||
|
self.assertEqual(Channel.objects.count(), 0)
|
||||||
|
|
||||||
|
# Session should now be clean
|
||||||
|
self.assertFalse("add_discord" in self.client.session)
|
@ -33,7 +33,7 @@ channel_urls = [
|
|||||||
views.add_pushbullet_complete,
|
views.add_pushbullet_complete,
|
||||||
name="hc-add-pushbullet-complete",
|
name="hc-add-pushbullet-complete",
|
||||||
),
|
),
|
||||||
path("add_discord/", views.add_discord, name="hc-add-discord"),
|
path("add_discord/", views.add_discord_complete, name="hc-add-discord-complete"),
|
||||||
path("add_pushover/", views.add_pushover, name="hc-add-pushover"),
|
path("add_pushover/", views.add_pushover, name="hc-add-pushover"),
|
||||||
path("telegram/bot/", views.telegram_bot, name="hc-telegram-webhook"),
|
path("telegram/bot/", views.telegram_bot, name="hc-telegram-webhook"),
|
||||||
path("add_telegram/", views.add_telegram, name="hc-add-telegram"),
|
path("add_telegram/", views.add_telegram, name="hc-add-telegram"),
|
||||||
@ -55,6 +55,7 @@ channel_urls = [
|
|||||||
|
|
||||||
project_urls = [
|
project_urls = [
|
||||||
path("add_apprise/", views.add_apprise, name="hc-add-apprise"),
|
path("add_apprise/", views.add_apprise, name="hc-add-apprise"),
|
||||||
|
path("add_discord/", views.add_discord, name="hc-add-discord"),
|
||||||
path("add_email/", views.add_email, name="hc-add-email"),
|
path("add_email/", views.add_email, name="hc-add-email"),
|
||||||
path("add_matrix/", views.add_matrix, name="hc-add-matrix"),
|
path("add_matrix/", views.add_matrix, name="hc-add-matrix"),
|
||||||
path("add_mattermost/", views.add_mattermost, name="hc-add-mattermost"),
|
path("add_mattermost/", views.add_mattermost, name="hc-add-mattermost"),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from datetime import datetime, timedelta as td
|
from datetime import datetime, timedelta as td
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from secrets import token_urlsafe
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from croniter import croniter
|
from croniter import croniter
|
||||||
@ -1108,7 +1109,7 @@ def add_pushbullet(request, code):
|
|||||||
project = _get_project_for_user(request, code)
|
project = _get_project_for_user(request, code)
|
||||||
redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet-complete")
|
redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet-complete")
|
||||||
|
|
||||||
state = get_random_string()
|
state = token_urlsafe()
|
||||||
authorize_url = "https://www.pushbullet.com/authorize?" + urlencode(
|
authorize_url = "https://www.pushbullet.com/authorize?" + urlencode(
|
||||||
{
|
{
|
||||||
"client_id": settings.PUSHBULLET_CLIENT_ID,
|
"client_id": settings.PUSHBULLET_CLIENT_ID,
|
||||||
@ -1140,7 +1141,7 @@ def add_pushbullet_complete(request):
|
|||||||
project = _get_project_for_user(request, code)
|
project = _get_project_for_user(request, code)
|
||||||
|
|
||||||
if request.GET.get("error") == "access_denied":
|
if request.GET.get("error") == "access_denied":
|
||||||
messages.warning(request, "Pushbullet setup was cancelled")
|
messages.warning(request, "Pushbullet setup was cancelled.")
|
||||||
return redirect("hc-p-channels", project.code)
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
if request.GET.get("state") != state:
|
if request.GET.get("state") != state:
|
||||||
@ -1170,55 +1171,72 @@ def add_pushbullet_complete(request):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_discord(request):
|
def add_discord(request, code):
|
||||||
if settings.DISCORD_CLIENT_ID is None:
|
if settings.DISCORD_CLIENT_ID is None:
|
||||||
raise Http404("discord integration is not available")
|
raise Http404("discord integration is not available")
|
||||||
|
|
||||||
redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord")
|
project = _get_project_for_user(request, code)
|
||||||
if "code" in request.GET:
|
redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord-complete")
|
||||||
code = _get_validated_code(request, "discord")
|
state = token_urlsafe()
|
||||||
if code is None:
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
result = requests.post(
|
|
||||||
"https://discordapp.com/api/oauth2/token",
|
|
||||||
{
|
|
||||||
"client_id": settings.DISCORD_CLIENT_ID,
|
|
||||||
"client_secret": settings.DISCORD_CLIENT_SECRET,
|
|
||||||
"code": code,
|
|
||||||
"grant_type": "authorization_code",
|
|
||||||
"redirect_uri": redirect_uri,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
doc = result.json()
|
|
||||||
if "access_token" in doc:
|
|
||||||
channel = Channel(kind="discord", project=request.project)
|
|
||||||
channel.user = request.project.owner
|
|
||||||
channel.value = result.text
|
|
||||||
channel.save()
|
|
||||||
channel.assign_all_checks()
|
|
||||||
messages.success(request, "The Discord integration has been added!")
|
|
||||||
else:
|
|
||||||
messages.warning(request, "Something went wrong")
|
|
||||||
|
|
||||||
return redirect("hc-channels")
|
|
||||||
|
|
||||||
auth_url = "https://discordapp.com/api/oauth2/authorize?" + urlencode(
|
auth_url = "https://discordapp.com/api/oauth2/authorize?" + urlencode(
|
||||||
{
|
{
|
||||||
"client_id": settings.DISCORD_CLIENT_ID,
|
"client_id": settings.DISCORD_CLIENT_ID,
|
||||||
"scope": "webhook.incoming",
|
"scope": "webhook.incoming",
|
||||||
"redirect_uri": redirect_uri,
|
"redirect_uri": redirect_uri,
|
||||||
"response_type": "code",
|
"response_type": "code",
|
||||||
"state": _prepare_state(request, "discord"),
|
"state": state,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx = {"page": "channels", "project": request.project, "authorize_url": auth_url}
|
ctx = {"page": "channels", "project": project, "authorize_url": auth_url}
|
||||||
|
|
||||||
|
request.session["add_discord"] = (state, str(project.code))
|
||||||
return render(request, "integrations/add_discord.html", ctx)
|
return render(request, "integrations/add_discord.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def add_discord_complete(request):
|
||||||
|
if settings.DISCORD_CLIENT_ID is None:
|
||||||
|
raise Http404("discord integration is not available")
|
||||||
|
|
||||||
|
if "add_discord" not in request.session:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
state, code = request.session.pop("add_discord")
|
||||||
|
project = _get_project_for_user(request, code)
|
||||||
|
|
||||||
|
if request.GET.get("error") == "access_denied":
|
||||||
|
messages.warning(request, "Discord setup was cancelled.")
|
||||||
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
|
if request.GET.get("state") != state:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord-complete")
|
||||||
|
result = requests.post(
|
||||||
|
"https://discordapp.com/api/oauth2/token",
|
||||||
|
{
|
||||||
|
"client_id": settings.DISCORD_CLIENT_ID,
|
||||||
|
"client_secret": settings.DISCORD_CLIENT_SECRET,
|
||||||
|
"code": request.GET.get("code"),
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"redirect_uri": redirect_uri,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
doc = result.json()
|
||||||
|
if "access_token" in doc:
|
||||||
|
channel = Channel(kind="discord", project=project)
|
||||||
|
channel.value = result.text
|
||||||
|
channel.save()
|
||||||
|
channel.assign_all_checks()
|
||||||
|
messages.success(request, "The Discord integration has been added!")
|
||||||
|
else:
|
||||||
|
messages.warning(request, "Something went wrong.")
|
||||||
|
|
||||||
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
|
|
||||||
def add_pushover(request):
|
def add_pushover(request):
|
||||||
if (
|
if (
|
||||||
settings.PUSHOVER_API_TOKEN is None
|
settings.PUSHOVER_API_TOKEN is None
|
||||||
|
@ -207,7 +207,7 @@
|
|||||||
|
|
||||||
<h2>Discord</h2>
|
<h2>Discord</h2>
|
||||||
<p>Cross-platform voice and text chat app designed for gamers.</p>
|
<p>Cross-platform voice and text chat app designed for gamers.</p>
|
||||||
<a href="{% url 'hc-add-discord' %}" class="btn btn-primary">Add Integration</a>
|
<a href="{% url 'hc-add-discord' project.code %}" class="btn btn-primary">Add Integration</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user