Initial release
authorEthan Nelson <git@ethan-nelson.com>
Sat, 17 Aug 2019 16:29:19 +0000 (09:29 -0700)
committerEthan Nelson <git@ethan-nelson.com>
Sat, 17 Aug 2019 16:29:19 +0000 (09:29 -0700)
README.md
barker.py [new file with mode: 0644]

index 47e50c6..181cc96 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,2 +1,25 @@
 Barker
 ======
+
+Barker is a python module for notifying other servers using the webhook protocol. Webhooks are a way for a web server to communicate out to another server without direct user interaction. They can be used for notifications or triggers to start other services.
+
+To use Barker, initialize the `Webhook` object:
+
+```
+from barker import Webhook
+hook = Webhook()
+```
+
+The `Webhook` object has a number of attributes set by the user:
+
+1. `url`: the url you want to send the webhook notification
+2. `data`: the information you want to post in the webhook
+3. `timeout`: the number of seconds to wait for a server response
+4. `key`: the hashing key used to verify message data. If not given, it defaults to `BARKER`.
+
+Additionally, some attributes are set by Barker:
+
+1. `headers`: custom HTTP headers containing information for message verification (`BARKER_SIGNATURE` and `BARKER_TIMESTAMP`)
+2. `response`: the response received from the server as a `requests.Response` object
+
+When a webhook is sent, it will include a signature in a header (`BARKER_SIGNATURE`) that is an HMAC hash of the time the webhook was generated (also a header, `BARKER_TIMESTAMP`) and the data joined by a ";". Local verification ensures the message is both correct and current. Note that hashing is based on escaped unicode characters.
diff --git a/barker.py b/barker.py
new file mode 100644 (file)
index 0000000..11e609e
--- /dev/null
+++ b/barker.py
@@ -0,0 +1,36 @@
+import hmac
+import requests
+import time
+__version__ = "0.1"
+
+
+class Webhook():
+    def __init__(self, url=None, data=None, timeout=5, key=None):
+        self.url = url
+        self.data = data
+        self.timeout = timeout
+        self.key = key if key is not None else 'BARKER'
+        self.headers = {}
+        self.response = None
+
+    def _calculate_hash(self):
+        """ Calculate hash to include as a header. """
+        self.headers['BARKER_TIMESTAMP'] = str(time.time())
+        hash = hmac.new(bytes(self.key, 'utf-8'))
+        hash.update(bytes(";".join((self.headers['BARKER_TIMESTAMP'], self.data)), 'utf-8'))
+        self.headers['BARKER_SIGNATURE'] = hash.hexdigest()
+
+    def send(self):
+        self._calculate_hash()
+        self.response = requests.post(self.url,
+                                      headers=self.headers,
+                                      data=self.data,
+                                      timeout=self.timeout)
+
+        return self.response
+
+    def verify_hash(self):
+        """ Verify the data matches the hash provided """
+        hash = hmac.new(bytes(self.key, 'utf-8'))
+        hash.update(bytes(";".join((self.headers['BARKER_TIMESTAMP'], self.data)), 'utf-8'))
+        return hmac.compare_digest(self.headers['BARKER_SIGNATURE'], hash.hexdigest())