Browse Source

Use client-side installation flow for installing HipChat integration.

pull/134/head
Pēteris Caune 7 years ago
parent
commit
012a86495f
9 changed files with 50 additions and 133 deletions
  1. +1
    -2
      hc/api/models.py
  2. +2
    -14
      hc/api/tests/test_channel_model.py
  3. +15
    -30
      hc/front/tests/test_add_hipchat.py
  4. +0
    -7
      hc/front/tests/test_channels.py
  5. +0
    -1
      hc/front/urls.py
  6. +24
    -30
      hc/front/views.py
  7. +1
    -6
      templates/front/channels.html
  8. +6
    -41
      templates/integrations/add_hipchat.html
  9. +1
    -2
      templates/integrations/hipchat_capabilities.json

+ 1
- 2
hc/api/models.py View File

@ -388,8 +388,7 @@ class Channel(models.Model):
if time.time() < doc.get("expires_at", 0): if time.time() < doc.get("expires_at", 0):
return # Current access token is still valid return # Current access token is still valid
endpoints = requests.get(doc["capabilitiesUrl"])
url = endpoints.json()["capabilities"]["oauth2Provider"]["tokenUrl"]
url = "https://api.hipchat.com/v2/oauth/token"
auth = (doc["oauthId"], doc["oauthSecret"]) auth = (doc["oauthId"], doc["oauthSecret"])
r = requests.post(url, auth=auth, data={ r = requests.post(url, auth=auth, data={
"grant_type": "client_credentials", "grant_type": "client_credentials",


+ 2
- 14
hc/api/tests/test_channel_model.py View File

@ -8,28 +8,16 @@ from mock import patch
class ChannelModelTestCase(BaseTestCase): class ChannelModelTestCase(BaseTestCase):
@patch("hc.api.models.requests.post") @patch("hc.api.models.requests.post")
@patch("hc.api.models.requests.get")
def test_it_refreshes_hipchat_access_token(self, mock_get, mock_post):
mock_get.return_value.json.return_value = {
"capabilities": {
"oauth2Provider": {"tokenUrl": "http://example.org"}
}
}
def test_it_refreshes_hipchat_access_token(self, mock_post):
mock_post.return_value.json.return_value = {"expires_in": 100} mock_post.return_value.json.return_value = {"expires_in": 100}
channel = Channel(kind="hipchat", user=self.alice, value=json.dumps({ channel = Channel(kind="hipchat", user=self.alice, value=json.dumps({
"oauthId": "foo", "oauthId": "foo",
"oauthSecret": "bar",
"capabilitiesUrl": "http://example.org/capabilities.json"
"oauthSecret": "bar"
})) }))
channel.refresh_hipchat_access_token() channel.refresh_hipchat_access_token()
# It should fetch the remote capabilities document
mock_get.assert_called()
# It should request a token using a correct tokenUrl # It should request a token using a correct tokenUrl
mock_post.assert_called() mock_post.assert_called()
args, kwargs = mock_post.call_args
self.assertEqual(args[0], "http://example.org")
self.assertTrue("expires_at" in channel.value) self.assertTrue("expires_at" in channel.value)

+ 15
- 30
hc/front/tests/test_add_hipchat.py View File

@ -1,6 +1,3 @@
import json
from django.core import signing
from hc.api.models import Channel from hc.api.models import Channel
from hc.test import BaseTestCase from hc.test import BaseTestCase
from mock import patch from mock import patch
@ -14,38 +11,26 @@ class AddHipChatTestCase(BaseTestCase):
r = self.client.get(self.url) r = self.client.get(self.url)
self.assertContains(r, "appropriate HipChat room") self.assertContains(r, "appropriate HipChat room")
def test_instructions_work_when_logged_out(self):
r = self.client.get(self.url)
self.assertContains(r, "Before adding HipChat integration, please")
def test_it_redirects_to_addons_install(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url)
self.assertEqual(r.status_code, 302)
def test_it_returns_capabilities(self): def test_it_returns_capabilities(self):
r = self.client.get("/integrations/hipchat/capabilities/") r = self.client.get("/integrations/hipchat/capabilities/")
self.assertContains(r, "callbackUrl")
self.assertContains(r, "installedUrl")
@patch("hc.api.models.Channel.refresh_hipchat_access_token")
def test_callback_works(self, mock_refresh):
state = signing.TimestampSigner().sign("alice")
payload = json.dumps({"relayState": state, "foo": "foobar"})
@patch("hc.front.views.Channel.refresh_hipchat_access_token")
@patch("hc.front.views.requests.get")
def test_it_adds_channel(self, mock_get, mock_refresh):
mock_get.return_value.json.return_value = {
"oauthId": "test-id"
}
mock_get.return_value.text = "{}"
r = self.client.post("/integrations/hipchat/callback/", payload,
content_type="application/json")
self.client.login(username="[email protected]", password="password")
s = "https://api.hipchat.com/foo"
r = self.client.post(self.url + "?installable_url=%s" % s)
self.assertEqual(r.status_code, 302)
self.assertEqual(r.status_code, 200)
self.assertTrue(mock_refresh.called)
c = Channel.objects.get() c = Channel.objects.get()
self.assertEqual(c.kind, "hipchat") self.assertEqual(c.kind, "hipchat")
self.assertTrue("foobar" in c.value)
@patch("hc.api.models.Channel.refresh_hipchat_access_token")
def test_callback_rejects_bad_signature(self, mock_refresh):
payload = json.dumps({"relayState": "alice:bad:sig", "foo": "foobar"})
r = self.client.post("/integrations/hipchat/callback/", payload,
content_type="application/json")
self.assertEqual(r.status_code, 400)
self.assertEqual(c.value, "{}")

+ 0
- 7
hc/front/tests/test_channels.py View File

@ -47,10 +47,3 @@ class ChannelsTestCase(BaseTestCase):
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
self.assertContains(r, "fake-key") self.assertContains(r, "fake-key")
self.assertContains(r, "(normal priority)") self.assertContains(r, "(normal priority)")
def test_it_shows_added_message(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/integrations/?added=hipchat")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "The HipChat integration has been added!")

+ 0
- 1
hc/front/urls.py View File

@ -21,7 +21,6 @@ channel_urls = [
url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"), url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"), url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),
url(r'^hipchat/capabilities/$', views.hipchat_capabilities, name="hc-hipchat-capabilities"), url(r'^hipchat/capabilities/$', views.hipchat_capabilities, name="hc-hipchat-capabilities"),
url(r'^hipchat/callback/$', views.hipchat_callback, name="hc-hipchat-callback"),
url(r'^add_pushbullet/$', views.add_pushbullet, name="hc-add-pushbullet"), url(r'^add_pushbullet/$', views.add_pushbullet, name="hc-add-pushbullet"),
url(r'^add_discord/$', views.add_discord, name="hc-add-discord"), url(r'^add_discord/$', views.add_discord, name="hc-add-discord"),
url(r'^add_pushover/$', views.add_pushover, name="hc-add-pushover"), url(r'^add_pushover/$', views.add_pushover, name="hc-add-pushover"),


+ 24
- 30
hc/front/views.py View File

@ -353,7 +353,6 @@ def channels(request):
"enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_sms": settings.TWILIO_AUTH is not None, "enable_sms": settings.TWILIO_AUTH is not None,
"enable_pd": settings.PD_VENDOR_KEY is not None, "enable_pd": settings.PD_VENDOR_KEY is not None,
"added": request.GET.get("added"),
"use_payments": settings.USE_PAYMENTS "use_payments": settings.USE_PAYMENTS
} }
@ -571,18 +570,34 @@ def add_slack_btn(request):
return redirect("hc-channels") return redirect("hc-channels")
@login_required
def add_hipchat(request): def add_hipchat(request):
if request.method == "POST":
username = request.team.user.username
state = signing.TimestampSigner().sign(username)
capabilities = settings.SITE_ROOT + reverse("hc-hipchat-capabilities")
if "installable_url" in request.GET:
url = request.GET["installable_url"]
assert url.startswith("https://api.hipchat.com")
response = requests.get(url)
if "oauthId" not in response.json():
messages.warning(request, "Something went wrong!")
return redirect("hc-channels")
url = "https://www.hipchat.com/addons/install?url=%s&relayState=%s" % \
(capabilities, state)
channel = Channel(kind="hipchat")
channel.user = request.team.user
channel.value = response.text
channel.save()
return redirect(url)
channel.refresh_hipchat_access_token()
channel.assign_all_checks()
messages.success(request, "The HipChat integration has been added!")
return redirect("hc-channels")
install_url = "https://www.hipchat.com/addons/install?" + urlencode({
"url": settings.SITE_ROOT + reverse("hc-hipchat-capabilities")
})
ctx = {"page": "channels"}
ctx = {
"page": "channels",
"install_url": install_url
}
return render(request, "integrations/add_hipchat.html", ctx) return render(request, "integrations/add_hipchat.html", ctx)
@ -591,27 +606,6 @@ def hipchat_capabilities(request):
content_type="application/json") content_type="application/json")
@csrf_exempt
@require_POST
def hipchat_callback(request):
doc = json.loads(request.body.decode("utf-8"))
try:
signer = signing.TimestampSigner()
username = signer.unsign(doc.get("relayState"), max_age=300)
except signing.BadSignature:
return HttpResponseBadRequest()
channel = Channel(kind="hipchat")
channel.user = User.objects.get(username=username)
channel.value = json.dumps(doc)
channel.save()
channel.refresh_hipchat_access_token()
channel.assign_all_checks()
return HttpResponse()
@login_required @login_required
def add_pushbullet(request): def add_pushbullet(request):
if settings.PUSHBULLET_CLIENT_ID is None: if settings.PUSHBULLET_CLIENT_ID is None:


+ 1
- 6
templates/front/channels.html View File

@ -6,16 +6,11 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
{% if messages or added %}
{% if messages %}
<div class="col-sm-12"> <div class="col-sm-12">
{% for message in messages %} {% for message in messages %}
<p class="alert alert-{{ message.tags }}">{{ message }}</p> <p class="alert alert-{{ message.tags }}">{{ message }}</p>
{% endfor %} {% endfor %}
{% if added == "hipchat" %}
<p class="alert alert-info">
The HipChat integration has been added!
</p>
{% endif %}
</div> </div>
{% endif %} {% endif %}


+ 6
- 41
templates/integrations/add_hipchat.html View File

@ -10,51 +10,17 @@
<h1>HipChat</h1> <h1>HipChat</h1>
<div class="jumbotron"> <div class="jumbotron">
{% if request.user.is_authenticated %}
<p>If your team uses <a href="https://www.hipchat.com/">HipChat</a>, <p>If your team uses <a href="https://www.hipchat.com/">HipChat</a>,
you can set up {% site_name %} to post status updates directly to an you can set up {% site_name %} to post status updates directly to an
appropriate HipChat room.</p> appropriate HipChat room.</p>
<div class="text-center"> <div class="text-center">
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-lg btn-primary">
Install HipChat Integration
</button>
</form>
</div>
{% else %}
<p>
{% site_name %} is a <strong>free</strong> and
<a href="https://github.com/healthchecks/healthchecks">open source</a>
service for monitoring your cron jobs, background processes and
scheduled tasks. Before adding HipChat integration, please log into
{% site_name %}:</p>
<div class="text-center">
<form class="form-inline" action="{% url 'hc-login' %}" method="post">
{% csrf_token %} {% csrf_token %}
<div class="form-group">
<div class="input-group input-group-lg">
<div class="input-group-addon">@</div>
<input
type="email"
class="form-control"
name="email"
autocomplete="email"
placeholder="Email">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-lg btn-primary pull-right">
Log In
</button>
</div>
</form>
<a href="{{ install_url }}" class="btn btn-default">
<img class="ai-icon" src="{% static 'img/integrations/hipchat.png' %}" alt="HipChat" />
Connect HipChat
</a>
</div> </div>
{% endif %}
</div> </div>
<h2>Setup Guide</h2> <h2>Setup Guide</h2>
@ -63,8 +29,8 @@
<div class="col-sm-6"> <div class="col-sm-6">
<span class="step-no">1</span> <span class="step-no">1</span>
<p> <p>
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
clicking on "Install HipChat Integration", you will be
After
clicking on "Connect HipChat", you will be
asked to log into HipChat. asked to log into HipChat.
</p> </p>
</div> </div>
@ -125,7 +91,6 @@
src="{% static 'img/integrations/setup_hipchat_4.png' %}"> src="{% static 'img/integrations/setup_hipchat_4.png' %}">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

+ 1
- 2
templates/integrations/hipchat_capabilities.json View File

@ -11,8 +11,7 @@
"installable": { "installable": {
"allowGlobal": false, "allowGlobal": false,
"allowRoom": true, "allowRoom": true,
"callbackUrl": "{% site_root %}{% url 'hc-hipchat-callback'%}",
"installedUrl": "{% site_root %}{% url 'hc-channels'%}?added=hipchat"
"installedUrl": "{% site_root %}{% url 'hc-add-hipchat'%}"
}, },
"hipchatApiConsumer": { "hipchatApiConsumer": {
"avatar": "{% site_root %}{% static 'img/logo-512-green.png' %}", "avatar": "{% site_root %}{% static 'img/logo-512-green.png' %}",


Loading…
Cancel
Save