aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkongr45gpen <electrovesta@gmail.com>2016-03-15 16:41:58 +0200
committerkongr45gpen <electrovesta@gmail.com>2016-03-15 16:41:58 +0200
commit23d851aff066a39e3ceb01f85c30539e467a70ea (patch)
tree2feeadac920c62161163b3b4d7ee0ae8361787b9
parente56a487b2a6672284a8f18a816782b5fdebf3b08 (diff)
downloadsupybot_github-23d851aff066a39e3ceb01f85c30539e467a70ea.tar.gz
supybot_github-23d851aff066a39e3ceb01f85c30539e467a70ea.tar.bz2
supybot_github-23d851aff066a39e3ceb01f85c30539e467a70ea.zip
Add support for github secrets
-rw-r--r--local/globals.py2
-rw-r--r--local/handler/GithubHandler.py37
-rw-r--r--local/utility.py22
-rw-r--r--plugin.py64
-rwxr-xr-xrequest.sh11
5 files changed, 126 insertions, 10 deletions
diff --git a/local/globals.py b/local/globals.py
index 6cc8c7b..b389cab 100644
--- a/local/globals.py
+++ b/local/globals.py
@@ -2,7 +2,9 @@ def init():
global messageList
global configOverrides
global travisStatuses
+ global secretDB
messageList = []
configOverrides = {}
travisStatuses = {}
+ secretDB = None
diff --git a/local/handler/GithubHandler.py b/local/handler/GithubHandler.py
index 4d420e3..53ea740 100644
--- a/local/handler/GithubHandler.py
+++ b/local/handler/GithubHandler.py
@@ -1,10 +1,12 @@
import os
import re
+import hmac
import json
import time
import random
import urllib
import urllib2
+import hashlib
import urlparse
import threading
import BaseHTTPServer
@@ -37,17 +39,12 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(s):
"""Respond to a POST request."""
length = int(s.headers['Content-Length'])
+ payload = s.rfile.read(length).decode('utf-8')
if 'content-type' not in s.headers or s.headers['content-type'] == 'application/x-www-form-urlencoded':
- post_data = urlparse.parse_qs(s.rfile.read(length).decode('utf-8'))
+ post_data = urlparse.parse_qs(payload)
data = json.loads(post_data['payload'][0])
else:
- data = json.loads(s.rfile.read(length).decode('utf-8'))
-
- s.send_response(200)
- s.send_header('Content-type', 'text/html')
- s.end_headers()
- s.wfile.write("Thanks, you're awesome.\n")
- s.wfile.write(s.path.split('/'))
+ data = json.loads(payload)
if 'X-GitHub-Event' in s.headers:
eventType = s.headers['X-GitHub-Event']
@@ -58,7 +55,7 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if not os.path.exists('requests/'):
os.makedirs('requests')
- f = open('requests/' + eventType + strftime("%Y-%m-%d %H:%M:%S") + '.json', 'w')
+ f = open('requests/' + eventType.replace('/','_') + strftime("%Y-%m-%d %H:%M:%S") + '.json', 'w')
f.write(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')))
f.close()
@@ -92,11 +89,33 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler):
i+=1
+ s.send_response(200)
+ s.send_header('Content-type', 'text/html')
+ s.end_headers()
+ s.wfile.write("Thanks, you're awesome.\n")
+ s.wfile.write(s.path.split('/'))
+
if requireCode and receivedcode != configValue('passcode'):
# The password is wrong
s.wfile.write("The password is wrong")
return
+ secret = getChannelSecret(channel)
+ if secret is not None:
+ if not 'X-Hub-Signature' in s.headers:
+ s.wfile.write("This channel requires a secret")
+ return
+
+ digest = "sha1=%s" % (hmac.new(secret, payload, hashlib.sha1).hexdigest(),)
+ log.debug("expected digest: %s", digest)
+
+ provided = s.headers['X-Hub-Signature']
+ log.debug("provided digest: %s", provided)
+
+ if not secureCompare(digest, provided):
+ s.wfile.write("Invalid secret key")
+ return
+
brackets = parseBrackets(configValue('brackets'))
themeName = configValue('theme')
diff --git a/local/utility.py b/local/utility.py
index de540a6..2594b61 100644
--- a/local/utility.py
+++ b/local/utility.py
@@ -1,5 +1,7 @@
import re
import math
+import random
+import string
import urllib2
import supybot.conf as conf
@@ -134,6 +136,26 @@ def isStatusVisible(repo, status):
globals.travisStatuses[repo] = status
return changed
+def randomString(length):
+ """Returns a securely generated random string of a specific length"""
+ return ''.join(random.SystemRandom().choice(
+ string.ascii_uppercase + string.ascii_lowercase + string.digits
+ ) for _ in range(length))
+
+def secureCompare(s1, s2):
+ """Securely compare two strings"""
+ return sum(i != j for i, j in zip(s1, s2)) is 0
+
+def getChannelSecret(channel):
+ """Returns a secret for a channel, or None if that channel has no secret"""
+ if globals.secretDB is None:
+ return None
+ try:
+ record = globals.secretDB.get(channel, 1)
+ return record.secret
+ except KeyError:
+ return None
+
def hexToMirc(hash):
colors = {
'white': (255, 255, 255),
diff --git a/plugin.py b/plugin.py
index 6153d62..89a4832 100644
--- a/plugin.py
+++ b/plugin.py
@@ -34,6 +34,7 @@ import urlparse
import threading
import BaseHTTPServer
+import supybot.dbi as dbi
import supybot.log as log
import supybot.conf as conf
import supybot.utils as utils
@@ -61,6 +62,11 @@ class Github(callbacks.Plugin):
messages = []
pass
+ add = None
+ search = None
+ stats = None
+ change = None
+
def ServerStart(self, httpd):
try:
log.info('Server Starts - %s:%s' % ('', self.port))
@@ -68,6 +74,7 @@ class Github(callbacks.Plugin):
except:
return
+
def __init__(self, irc):
self.__parent = super(Github, self)
self.__parent.__init__(irc)
@@ -117,6 +124,63 @@ class Github(callbacks.Plugin):
# Debug command
get = wrap(get, ['lowered', optional('lowered'), optional('text')]) if world.testing else False
+ def abs_val(self,irc,msg,args):
+ pass
+ abs_val = wrap(abs_val,[])
+
+ class secret(callbacks.Commands):
+ class DB(plugins.DbiChannelDB):
+ class DB(dbi.DB):
+ class Record(dbi.Record):
+ __fields__ = [
+ 'secret'
+ ]
+ def add(self, secret, **kwargs):
+ record = self.Record(secret = secret, **kwargs)
+ return super(self.__class__, self).add(record)
+ def set(self, id, secret, **kwargs):
+ record = self.Record(secret = secret, **kwargs)
+ return super(self.__class__, self).set(id, record)
+
+ def __init__(self, irc):
+ # self.db = self.DB(("%s-secret") % (self.name(),))
+ print(conf.supybot.databases())
+ super(Github.secret, self).__init__(irc)
+ self.db = plugins.DB(("github-secret"), {'flat': self.DB})()
+ globals.secretDB = self.db
+
+ def set(self, irc, msg, args, channel, secret):
+ """[<channel>] secret
+
+ Sets a Github secret for a channel to a specific value.
+ <channel> is only necessary if the message isn't sent in the channel itself.
+ """
+ self.db.set(channel, 1, secret)
+ # record = Github.secret.DB.DB.Record(secret = sec)
+ # self.db.set(channel, 1, secret)
+ irc.replySuccess()
+ set = wrap(set, ['channel', 'text'])
+
+ def reset(self, irc, msg, args, channel):
+ """[<channel>]
+
+ Removes a Github secret for a channel.
+ <channel> is only necessary if the message isn't sent in the channel itself.
+ """
+ self.db.remove(channel, 1)
+ irc.reply("Channel %s no longer has a secret." % channel)
+ reset = wrap(reset, ['channel'])
+
+ def generate(self, irc, msg, args, channel):
+ """<channel>
+
+ Generates a Github secret for a channel.
+ <channel> is only necessary if the message isn't sent in the channel itself.
+ """
+ secret = Utility.randomString(40)
+ irc.reply("Setting secret for %s to: %s" % (channel, secret))
+ self.db.set(channel, 1, secret)
+ generate = wrap(generate, ['channel'])
Class = Github
diff --git a/request.sh b/request.sh
index f118034..61864e7 100755
--- a/request.sh
+++ b/request.sh
@@ -1,3 +1,12 @@
#!/usr/bin/env bash
-curl --data "payload=`cat $1`" http://localhost:8093/
+DATA="payload=`cat $1`"
+
+echo $DATA
+
+hash=$(echo -n "$DATA" | openssl dgst -sha1 -hmac "$2" | awk '{print $2}')
+echo $hash
+
+echo -n "$DATA" > ~/repos/supybot-github/cmpr
+
+curl --header "X-GitHub-Event: $1" --header "X-Hub-Signature: sha1=$hash" --data "$DATA" http://localhost:8093/

© 2014-2024 Faster IT GmbH | imprint | privacy policy