activitypub: implement HTTP Signatures using httpsig library

thanks @ahknight!
pull/27/head
Ryan Barrett 2017-09-19 07:15:38 -07:00
rodzic 2699de6903
commit b123088158
3 zmienionych plików z 30 dodań i 6 usunięć

Wyświetl plik

@ -3,6 +3,7 @@
bs4 bs4
feedparser feedparser
granary>=1.8 granary>=1.8
httpsig
mf2py>=1.0.4 mf2py>=1.0.4
mf2util>=0.5.0 mf2util>=0.5.0
mock mock

Wyświetl plik

@ -13,6 +13,7 @@ import urllib2
from django_salmon import magicsigs, utils from django_salmon import magicsigs, utils
import feedparser import feedparser
from granary import atom, microformats2 from granary import atom, microformats2
from httpsig.sign import HeaderSigner
import mf2py import mf2py
import mock import mock
from mock import call from mock import call
@ -105,9 +106,12 @@ class WebmentionTest(testutil.TestCase):
], ],
}, kwargs['json']) }, kwargs['json'])
expected_headers = copy.copy(common.HEADERS) headers = kwargs['headers']
expected_headers['Content-Type'] = activitypub.CONTENT_TYPE_AS self.assertEqual(activitypub.CONTENT_TYPE_AS, headers['Content-Type'])
self.assertEqual(expected_headers, kwargs['headers'])
expected_key = MagicKey.get_by_id('a')
rsa_key = kwargs['auth'].header_signer._rsa._key
self.assertEqual(expected_key.private_pem(), rsa_key.exportKey())
def test_salmon(self, mock_get, mock_post): def test_salmon(self, mock_get, mock_post):
orig_atom = requests_response("""\ orig_atom = requests_response("""\

Wyświetl plik

@ -4,6 +4,7 @@ TODO tests:
* actor/attributedTo could be string URL * actor/attributedTo could be string URL
* salmon rel via webfinger via author.name + domain * salmon rel via webfinger via author.name + domain
""" """
import datetime
import json import json
import logging import logging
import urlparse import urlparse
@ -15,6 +16,7 @@ import django_salmon
from django_salmon import magicsigs, utils from django_salmon import magicsigs, utils
import feedparser import feedparser
from granary import atom, microformats2 from granary import atom, microformats2
from httpsig.requests_auth import HTTPSignatureAuth
import mf2py import mf2py
import mf2util import mf2util
from oauth_dropins.webutil import util from oauth_dropins.webutil import util
@ -93,10 +95,27 @@ class WebmentionHandler(webapp2.RequestHandler):
source_obj['inReplyTo'], source_obj['inReplyTo'],
]) ])
# deliver source object to target actor's inbox # prepare HTTP Signature (required by Mastodon)
# https://w3c.github.io/activitypub/#authorization-lds
# https://tools.ietf.org/html/draft-cavage-http-signatures-07
# https://github.com/tootsuite/mastodon/issues/4906#issuecomment-328844846
source_domain = urlparse.urlparse(source).netloc
key = models.MagicKey.get_or_create(source_domain)
acct = 'acct:me@%s' % source_domain
auth = HTTPSignatureAuth(secret=key.private_pem(), key_id=acct,
algorithm='rsa-sha256')
# deliver source object to target actor's inbox.
headers = {
'Content-Type': activitypub.CONTENT_TYPE_AS,
# required for HTTP Signature
# https://tools.ietf.org/html/draft-cavage-http-signatures-07#section-2.1.3
'Date': datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'),
}
resp = common.requests_post( resp = common.requests_post(
urlparse.urljoin(target, inbox_url), json=source_obj, urlparse.urljoin(target, inbox_url), json=source_obj, auth=auth,
headers={'Content-Type': activitypub.CONTENT_TYPE_AS}, log=True) headers=headers, log=True)
def send_salmon(self, source_obj, target_url=None, target_resp=None): def send_salmon(self, source_obj, target_url=None, target_resp=None):
# fetch target HTML page, extract Atom rel-alternate link # fetch target HTML page, extract Atom rel-alternate link