rename MagicKey => Domain, Response => Activity

the Python classes, but not (yet) the kinds in the datastore. maybe eventually.
pull/280/head
Ryan Barrett 2022-11-12 15:27:59 -08:00
rodzic 53a133d554
commit 4f3dc03a3e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
21 zmienionych plików z 155 dodań i 144 usunięć

Wyświetl plik

@ -17,7 +17,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
from app import app, cache from app import app, cache
import common import common
from common import redirect_unwrap, redirect_wrap from common import redirect_unwrap, redirect_wrap
from models import Follower, MagicKey from models import Follower, Domain
from httpsig.requests_auth import HTTPSignatureAuth from httpsig.requests_auth import HTTPSignatureAuth
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -58,7 +58,7 @@ def send(activity, inbox_url, user_domain):
# https://tools.ietf.org/html/draft-cavage-http-signatures-07 # https://tools.ietf.org/html/draft-cavage-http-signatures-07
# https://github.com/tootsuite/mastodon/issues/4906#issuecomment-328844846 # https://github.com/tootsuite/mastodon/issues/4906#issuecomment-328844846
key_id = request.host_url + user_domain key_id = request.host_url + user_domain
key = MagicKey.get_or_create(user_domain) key = Domain.get_or_create(user_domain)
auth = HTTPSignatureAuth(secret=key.private_pem(), key_id=key_id, auth = HTTPSignatureAuth(secret=key.private_pem(), key_id=key_id,
algorithm='rsa-sha256', sign_header='signature', algorithm='rsa-sha256', sign_header='signature',
headers=('Date', 'Digest', 'Host')) headers=('Date', 'Digest', 'Host'))
@ -94,7 +94,7 @@ def actor(domain):
if not hcard: if not hcard:
error(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}") error(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}")
key = MagicKey.get_or_create(domain) key = Domain.get_or_create(domain)
obj = common.postprocess_as2( obj = common.postprocess_as2(
as2.from_as1(microformats2.json_to_object(hcard)), key=key) as2.from_as1(microformats2.json_to_object(hcard)), key=key)
obj.update({ obj.update({

Wyświetl plik

@ -14,7 +14,7 @@ from oauth_dropins.webutil.flask_util import error
import requests import requests
from werkzeug.exceptions import BadGateway from werkzeug.exceptions import BadGateway
from models import Response from models import Activity
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -144,11 +144,11 @@ def content_type(resp):
return type.split(';')[0] return type.split(';')[0]
def send_webmentions(activity_wrapped, proxy=None, **response_props): def send_webmentions(activity_wrapped, proxy=None, **activity_props):
"""Sends webmentions for an incoming Salmon slap or ActivityPub inbox delivery. """Sends webmentions for an incoming Salmon slap or ActivityPub inbox delivery.
Args: Args:
activity_wrapped: dict, AS1 activity activity_wrapped: dict, AS1 activity
response_props: passed through to the newly created Responses activity_props: passed through to the newly created Activity entities
""" """
activity = redirect_unwrap(activity_wrapped) activity = redirect_unwrap(activity_wrapped)
@ -186,7 +186,7 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props):
if not targets: if not targets:
error("Couldn't find any target URLs in inReplyTo, object, or mention tags") error("Couldn't find any target URLs in inReplyTo, object, or mention tags")
# send webmentions and store Responses # send webmentions and store Activitys
errors = [] # stores (code, body) tuples errors = [] # stores (code, body) tuples
for target in targets: for target in targets:
domain = util.domain_from_link(target, minimize=False) domain = util.domain_from_link(target, minimize=False)
@ -194,10 +194,10 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props):
logger.info(f'Skipping same-domain webmention from {source} to {target}') logger.info(f'Skipping same-domain webmention from {source} to {target}')
continue continue
response = Response(source=source, target=target, direction='in', activity = Activity(source=source, target=target, direction='in',
domain=domain, **response_props) domain=domain, **activity_props)
response.put() activity.put()
wm_source = (response.proxy_url() wm_source = (activity.proxy_url()
if verb in ('follow', 'like', 'share') or proxy if verb in ('follow', 'like', 'share') or proxy
else source) else source)
logger.info(f'Sending webmention from {wm_source} to {target}') logger.info(f'Sending webmention from {wm_source} to {target}')
@ -206,14 +206,14 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props):
endpoint = webmention.discover(target).endpoint endpoint = webmention.discover(target).endpoint
if endpoint: if endpoint:
webmention.send(endpoint, wm_source, target) webmention.send(endpoint, wm_source, target)
response.status = 'complete' activity.status = 'complete'
logger.info('Success!') logger.info('Success!')
else: else:
response.status = 'ignored' activity.status = 'ignored'
logger.info('Ignoring.') logger.info('Ignoring.')
except BaseException as e: except BaseException as e:
errors.append(util.interpret_http_exception(e)) errors.append(util.interpret_http_exception(e))
response.put() activity.put()
if errors: if errors:
msg = 'Errors: ' + ', '.join(f'{code} {body}' for code, body in errors) msg = 'Errors: ' + ', '.join(f'{code} {body}' for code, body in errors)
@ -227,7 +227,7 @@ def postprocess_as2(activity, target=None, key=None):
activity: dict, AS2 object or activity activity: dict, AS2 object or activity
target: dict, AS2 object, optional. The target of activity's inReplyTo or target: dict, AS2 object, optional. The target of activity's inReplyTo or
Like/Announce/etc object, if any. Like/Announce/etc object, if any.
key: :class:`models.MagicKey`, optional. populated into publicKey field key: :class:`models.Domain`, optional. populated into publicKey field
if provided. if provided.
""" """
type = activity.get('type') type = activity.get('type')

Wyświetl plik

@ -11,7 +11,7 @@ from oauth_dropins.webutil.models import StringIdModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MagicKey(StringIdModel): class Domain(StringIdModel):
"""Stores a user's public/private key pair used for Magic Signatures. """Stores a user's public/private key pair used for Magic Signatures.
The key name is the domain. The key name is the domain.
@ -28,17 +28,21 @@ class MagicKey(StringIdModel):
public_exponent = ndb.StringProperty(required=True) public_exponent = ndb.StringProperty(required=True)
private_exponent = ndb.StringProperty(required=True) private_exponent = ndb.StringProperty(required=True)
@classmethod
def _get_kind(cls):
return 'MagicKey'
@staticmethod @staticmethod
@ndb.transactional() @ndb.transactional()
def get_or_create(domain): def get_or_create(domain):
"""Loads and returns a MagicKey. Creates it if necessary.""" """Loads and returns a Domain. Creates it if necessary."""
key = MagicKey.get_by_id(domain) key = Domain.get_by_id(domain)
if not key: if not key:
# this uses urandom(), and does nontrivial math, so it can take a # this uses urandom(), and does nontrivial math, so it can take a
# while depending on the amount of randomness available. # while depending on the amount of randomness available.
pubexp, mod, privexp = magicsigs.generate() pubexp, mod, privexp = magicsigs.generate()
key = MagicKey(id=domain, mod=mod, public_exponent=pubexp, key = Domain(id=domain, mod=mod, public_exponent=pubexp,
private_exponent=privexp) private_exponent=privexp)
key.put() key.put()
@ -62,7 +66,7 @@ class MagicKey(StringIdModel):
return rsa.exportKey(format='PEM') return rsa.exportKey(format='PEM')
class Response(StringIdModel): class Activity(StringIdModel):
"""A reply, like, repost, or other interaction that we've relayed. """A reply, like, repost, or other interaction that we've relayed.
Key name is 'SOURCE_URL TARGET_URL', e.g. 'http://a/reply http://orig/post'. Key name is 'SOURCE_URL TARGET_URL', e.g. 'http://a/reply http://orig/post'.
@ -86,16 +90,20 @@ class Response(StringIdModel):
created = ndb.DateTimeProperty(auto_now_add=True) created = ndb.DateTimeProperty(auto_now_add=True)
updated = ndb.DateTimeProperty(auto_now=True) updated = ndb.DateTimeProperty(auto_now=True)
@classmethod
def _get_kind(cls):
return 'Response'
def __init__(self, source=None, target=None, **kwargs): def __init__(self, source=None, target=None, **kwargs):
if source and target: if source and target:
assert 'id' not in kwargs assert 'id' not in kwargs
kwargs['id'] = self._id(source, target) kwargs['id'] = self._id(source, target)
logger.info(f"Response id (source target): {kwargs['id']}") logger.info(f"Activity id (source target): {kwargs['id']}")
super(Response, self).__init__(**kwargs) super(Activity, self).__init__(**kwargs)
@classmethod @classmethod
def get_or_create(cls, source=None, target=None, **kwargs): def get_or_create(cls, source=None, target=None, **kwargs):
logger.info(f'Response source target: {source} {target}') logger.info(f'Activity source target: {source} {target}')
return cls.get_or_insert(cls._id(source, target), **kwargs) return cls.get_or_insert(cls._id(source, target), **kwargs)
def source(self): def source(self):
@ -105,7 +113,7 @@ class Response(StringIdModel):
return self.key.id().split()[1] return self.key.id().split()[1]
def proxy_url(self): def proxy_url(self):
"""Returns the Bridgy Fed proxy URL to render this response as HTML.""" """Returns the Bridgy Fed proxy URL to render this post as HTML."""
if self.source_mf2 or self.source_as2 or self.source_atom: if self.source_mf2 or self.source_as2 or self.source_atom:
source, target = self.key.id().split(' ') source, target = self.key.id().split(' ')
return f'{request.host_url}render?' + urllib.parse.urlencode({ return f'{request.host_url}render?' + urllib.parse.urlencode({

Wyświetl plik

@ -1,4 +1,4 @@
"""Render recent responses and logs.""" """UI pages."""
import calendar import calendar
import datetime import datetime
from itertools import islice from itertools import islice
@ -12,7 +12,7 @@ from oauth_dropins.webutil.flask_util import error
from app import app, cache from app import app, cache
import common import common
from models import Follower, MagicKey, Response from models import Follower, Domain, Activity
PAGE_SIZE = 20 PAGE_SIZE = 20
FOLLOWERS_UI_LIMIT = 999 FOLLOWERS_UI_LIMIT = 999
@ -28,14 +28,14 @@ def front_page():
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>') @app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>')
@app.get(f'/responses/<regex("{common.DOMAIN_RE}"):domain>') # deprecated @app.get(f'/responses/<regex("{common.DOMAIN_RE}"):domain>') # deprecated
def user(domain): def user(domain):
if not MagicKey.get_by_id(domain): if not Domain.get_by_id(domain):
return render_template('user_not_found.html', domain=domain), 404 return render_template('user_not_found.html', domain=domain), 404
query = Response.query( query = Activity.query(
Response.status.IN(('new', 'complete', 'error')), Activity.status.IN(('new', 'complete', 'error')),
Response.domain == domain, Activity.domain == domain,
) )
responses, before, after = fetch_page(query, Response) activities, before, after = fetch_page(query, Activity)
followers = Follower.query(Follower.dest == domain)\ followers = Follower.query(Follower.dest == domain)\
.count(limit=FOLLOWERS_UI_LIMIT) .count(limit=FOLLOWERS_UI_LIMIT)
@ -54,7 +54,10 @@ def user(domain):
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/followers') @app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/followers')
def followers(domain): def followers(domain):
if not MagicKey.get_by_id(domain): # TODO:
# pull more info from last_follow, eg name, profile picture, url
# unify with following
if not Domain.get_by_id(domain):
return render_template('user_not_found.html', domain=domain), 404 return render_template('user_not_found.html', domain=domain), 404
query = Follower.query( query = Follower.query(
@ -79,7 +82,7 @@ def followers(domain):
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/following') @app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/following')
def following(domain): def following(domain):
if not MagicKey.get_by_id(domain): if not Domain.get_by_id(domain):
return render_template('user_not_found.html', domain=domain), 404 return render_template('user_not_found.html', domain=domain), 404
query = Follower.query( query = Follower.query(
@ -105,9 +108,9 @@ def following(domain):
@app.get('/recent') @app.get('/recent')
@app.get('/responses') # deprecated @app.get('/responses') # deprecated
def recent(): def recent():
"""Renders recent Responses, with links to logs.""" """Renders recent activities, with links to logs."""
query = Response.query(Response.status.IN(('new', 'complete', 'error'))) query = Activity.query(Activity.status.IN(('new', 'complete', 'error')))
responses, before, after = fetch_page(query, Response) activities, before, after = fetch_page(query, Activity)
return render_template( return render_template(
'recent.html', 'recent.html',
util=util, util=util,
@ -186,8 +189,8 @@ def fetch_page(query, model_class):
def stats(): def stats():
return render_template( return render_template(
'stats.html', 'stats.html',
users=KindStat.query(KindStat.kind_name == 'MagicKey').get().count, users=KindStat.query(KindStat.kind_name == 'Domain').get().count,
responses=KindStat.query(KindStat.kind_name == 'Response').get().count, activities=KindStat.query(KindStat.kind_name == 'Activity').get().count,
followers=KindStat.query(KindStat.kind_name == 'Follower').get().count, followers=KindStat.query(KindStat.kind_name == 'Follower').get().count,
) )

Wyświetl plik

@ -23,7 +23,7 @@ from werkzeug.exceptions import abort
from app import app, cache from app import app, cache
import common import common
from models import MagicKey from models import Domain
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -51,8 +51,8 @@ def redir(to):
util.domain_from_link(to, minimize=False), util.domain_from_link(to, minimize=False),
urllib.parse.urlparse(to).hostname)) urllib.parse.urlparse(to).hostname))
for domain in domains: for domain in domains:
if domain and MagicKey.get_by_id(domain): if domain and Domain.get_by_id(domain):
logger.info(f'Found MagicKey for domain {domain}') logger.info(f'Found Domain for domain {domain}')
break break
else: else:
logger.info(f'No user found for any of {domains}; returning 404') logger.info(f'No user found for any of {domains}; returning 404')

Wyświetl plik

@ -1,5 +1,5 @@
# coding=utf-8 # coding=utf-8
"""Renders mf2 proxy pages based on stored Responses.""" """Renders mf2 proxy pages based on stored Activity entities."""
import datetime import datetime
from flask import request from flask import request
@ -10,7 +10,7 @@ from oauth_dropins.webutil.util import json_loads
from app import app, cache from app import app, cache
import common import common
from models import Response from models import Activity
CACHE_TIME = datetime.timedelta(minutes=15) CACHE_TIME = datetime.timedelta(minutes=15)
@ -18,23 +18,23 @@ CACHE_TIME = datetime.timedelta(minutes=15)
@app.get('/render') @app.get('/render')
@flask_util.cached(cache, CACHE_TIME) @flask_util.cached(cache, CACHE_TIME)
def render(): def render():
"""Fetches a stored Response and renders it as HTML.""" """Fetches a stored Activity and renders it as HTML."""
source = flask_util.get_required_param('source') source = flask_util.get_required_param('source')
target = flask_util.get_required_param('target') target = flask_util.get_required_param('target')
id = f'{source} {target}' id = f'{source} {target}'
resp = Response.get_by_id(id) activity = Activity.get_by_id(id)
if not resp: if not activity:
error(f'No stored response for {id}', status=404) error(f'No stored activity for {id}', status=404)
if resp.source_mf2: if activity.source_mf2:
as1 = microformats2.json_to_object(json_loads(resp.source_mf2)) as1 = microformats2.json_to_object(json_loads(activity.source_mf2))
elif resp.source_as2: elif activity.source_as2:
as1 = as2.to_as1(json_loads(resp.source_as2)) as1 = as2.to_as1(json_loads(activity.source_as2))
elif resp.source_atom: elif activity.source_atom:
as1 = atom.atom_to_activity(resp.source_atom) as1 = atom.atom_to_activity(activity.source_atom)
else: else:
error(f'Stored response for {id} has no data', status=404) error(f'Stored activity for {id} has no data', status=404)
# add HTML meta redirect to source page. should trigger for end users in # add HTML meta redirect to source page. should trigger for end users in
# browsers but not for webmention receivers (hopefully). # browsers but not for webmention receivers (hopefully).

Wyświetl plik

@ -0,0 +1,23 @@
<!-- <table> -->
<!-- <tr><th>Source</th> <th>Target</th> <th>Protocol</th> <th>Status</th> <th>Time (click for log)</th></tr> -->
<br>
{% for a in activities %}
<div class="row">
<div class="col-sm-3">{{ util.pretty_link(a.source())|safe }}</div>
<div class="col-sm-3">{{ util.pretty_link(a.target())|safe }}</div>
<div class="col-sm-2">{{ a.protocol }}</div>
<div class="col-sm-2">{{ a.status }}</div>
<div class="col-sm-2">
{% if a.log_url_path %}<a href="{{ a.log_url_path }}">{% endif %}
{{ a.updated.replace(microsecond=0) }}
{% if a.log_url_path %}</a>{% endif %}
</div>
</div>
{% else %}
<div class="row">None</div>
{% endfor %}
<!-- </table> -->
{% include "paging.html" %}

Wyświetl plik

@ -1,13 +1,13 @@
<div class="row"> <div class="row">
<div class="col-sm-3"> <div class="col-sm-3">
{% if after %} {% if after %}
<a href="?after={{ after }}#responses">&larr; Newer</a> <a href="?after={{ after }}">&larr; Newer</a>
{% endif %} {% endif %}
</div> </div>
<div class="col-sm-3 col-sm-offset-6"> <div class="col-sm-3 col-sm-offset-6">
{% if before %} {% if before %}
<a href="?before={{ before }}#responses">Older &rarr;</a> <a href="?before={{ before }}">Older &rarr;</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>

Wyświetl plik

@ -6,6 +6,6 @@
<h2 class="row">Recent activity</h3> <h2 class="row">Recent activity</h3>
{% include "responses.html" %} {% include "activities.html" %}
{% endblock %} {% endblock %}

Wyświetl plik

@ -1,23 +0,0 @@
<!-- <table> -->
<!-- <tr><th>Source</th> <th>Target</th> <th>Protocol</th> <th>Status</th> <th>Time (click for log)</th></tr> -->
<br>
{% for r in responses %}
<div class="row">
<div class="col-sm-3">{{ util.pretty_link(r.source())|safe }}</div>
<div class="col-sm-3">{{ util.pretty_link(r.target())|safe }}</div>
<div class="col-sm-2">{{ r.protocol }}</div>
<div class="col-sm-2">{{ r.status }}</div>
<div class="col-sm-2">
{% if r.log_url_path %}<a href="{{ r.log_url_path }}">{% endif %}
{{ r.updated.replace(microsecond=0) }}
{% if r.log_url_path %}</a>{% endif %}
</div>
</div>
{% else %}
<div class="row">None</div>
{% endfor %}
<!-- </table> -->
{% include "paging.html" %}

Wyświetl plik

@ -12,7 +12,7 @@
<li><a href="https://snarfed.org/2017-10-22_bridgy-fed">Launched Octover 17, 2022</a> <li><a href="https://snarfed.org/2017-10-22_bridgy-fed">Launched Octover 17, 2022</a>
<li>{{ users }} users</li> <li>{{ users }} users</li>
<li>{{ followers }} fediverse followers</li> <li>{{ followers }} fediverse followers</li>
<li>{{ responses }} activities handled</li> <li>{{ activities }} activities handled</li>
</ul> </ul>
</main> </main>
</body> </body>

Wyświetl plik

@ -16,6 +16,6 @@
<h3 class="row">Recent activity</h3> <h3 class="row">Recent activity</h3>
{% include "responses.html" %} {% include "activities.html" %}
{% endblock %} {% endblock %}

Wyświetl plik

@ -14,7 +14,7 @@ from urllib3.exceptions import ReadTimeoutError
import activitypub import activitypub
import common import common
from models import Follower, MagicKey, Response from models import Follower, Domain, Activity
from . import testutil from . import testutil
REPLY_OBJECT = { REPLY_OBJECT = {
@ -173,7 +173,7 @@ class ActivityPubTest(testutil.TestCase):
'publicKey': { 'publicKey': {
'id': 'http://localhost/foo.com', 'id': 'http://localhost/foo.com',
'owner': 'http://localhost/foo.com', 'owner': 'http://localhost/foo.com',
'publicKeyPem': MagicKey.get_by_id('foo.com').public_pem().decode(), 'publicKeyPem': Domain.get_by_id('foo.com').public_pem().decode(),
}, },
}, got.json) }, got.json)
@ -237,7 +237,7 @@ class ActivityPubTest(testutil.TestCase):
}, },
) )
resp = Response.get_by_id('http://this/reply http://orig/post') resp = Activity.get_by_id('http://this/reply http://orig/post')
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -257,7 +257,7 @@ class ActivityPubTest(testutil.TestCase):
self.assert_req(mock_head, 'http://this', allow_redirects=True) self.assert_req(mock_head, 'http://this', allow_redirects=True)
mock_get.assert_not_called() mock_get.assert_not_called()
mock_post.assert_not_called() mock_post.assert_not_called()
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
def test_inbox_mention_object(self, *mocks): def test_inbox_mention_object(self, *mocks):
self._test_inbox_mention(MENTION_OBJECT, *mocks) self._test_inbox_mention(MENTION_OBJECT, *mocks)
@ -286,7 +286,7 @@ class ActivityPubTest(testutil.TestCase):
}, },
) )
resp = Response.get_by_id('http://this/mention http://target/') resp = Activity.get_by_id('http://this/mention http://target/')
self.assertEqual('target', resp.domain) self.assertEqual('target', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -319,7 +319,7 @@ class ActivityPubTest(testutil.TestCase):
'target': 'http://orig/post', 'target': 'http://orig/post',
}, kwargs['data']) }, kwargs['data'])
resp = Response.get_by_id('http://this/like__ok http://orig/post') resp = Activity.get_by_id('http://this/like__ok http://orig/post')
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -358,7 +358,7 @@ class ActivityPubTest(testutil.TestCase):
'target': 'https://www.realize.be/', 'target': 'https://www.realize.be/',
}, kwargs['data']) }, kwargs['data'])
resp = Response.get_by_id('https://mastodon.social/6d1a https://www.realize.be/') resp = Activity.get_by_id('https://mastodon.social/6d1a https://www.realize.be/')
self.assertEqual('www.realize.be', resp.domain) self.assertEqual('www.realize.be', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -443,7 +443,7 @@ class ActivityPubTest(testutil.TestCase):
got = self.client.post('/foo.com/inbox', json=LIKE) got = self.client.post('/foo.com/inbox', json=LIKE)
self.assertEqual(200, got.status_code) self.assertEqual(200, got.status_code)
resp = Response.get_by_id('http://this/like__ok http://orig/post') resp = Activity.get_by_id('http://this/like__ok http://orig/post')
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)

Wyświetl plik

@ -1,22 +1,22 @@
# coding=utf-8 # coding=utf-8
"""Unit tests for models.py.""" """Unit tests for models.py."""
from app import app from app import app
from models import MagicKey, Response from models import Domain, Activity
from . import testutil from . import testutil
class MagicKeyTest(testutil.TestCase): class DomainTest(testutil.TestCase):
def setUp(self): def setUp(self):
super(MagicKeyTest, self).setUp() super(DomainTest, self).setUp()
self.key = MagicKey.get_or_create('y.z') self.key = Domain.get_or_create('y.z')
def test_magic_key_get_or_create(self): def test_magic_key_get_or_create(self):
assert self.key.mod assert self.key.mod
assert self.key.public_exponent assert self.key.public_exponent
assert self.key.private_exponent assert self.key.private_exponent
same = MagicKey.get_or_create('y.z') same = Domain.get_or_create('y.z')
self.assertEqual(same, self.key) self.assertEqual(same, self.key)
def test_href(self): def test_href(self):
@ -36,25 +36,25 @@ class MagicKeyTest(testutil.TestCase):
self.assertTrue(pem.decode().endswith('-----END RSA PRIVATE KEY-----'), pem) self.assertTrue(pem.decode().endswith('-----END RSA PRIVATE KEY-----'), pem)
class ResponseTest(testutil.TestCase): class ActivityTest(testutil.TestCase):
def test_constructor(self): def test_constructor(self):
resp = Response('abc', 'xyz') resp = Activity('abc', 'xyz')
self.assertEqual('abc xyz', resp.key.id()) self.assertEqual('abc xyz', resp.key.id())
resp = Response('abc#1', 'xyz#Z') resp = Activity('abc#1', 'xyz#Z')
self.assertEqual('abc__1 xyz__Z', resp.key.id()) self.assertEqual('abc__1 xyz__Z', resp.key.id())
def test_get_or_create(self): def test_get_or_create(self):
resp = Response.get_or_create('abc', 'xyz') resp = Activity.get_or_create('abc', 'xyz')
self.assertEqual('abc xyz', resp.key.id()) self.assertEqual('abc xyz', resp.key.id())
resp = Response.get_or_create('abc#1', 'xyz#Z') resp = Activity.get_or_create('abc#1', 'xyz#Z')
self.assertEqual('abc__1 xyz__Z', resp.key.id()) self.assertEqual('abc__1 xyz__Z', resp.key.id())
def test_proxy_url(self): def test_proxy_url(self):
with app.test_request_context('/'): with app.test_request_context('/'):
resp = Response.get_or_create('abc', 'xyz') resp = Activity.get_or_create('abc', 'xyz')
self.assertIsNone(resp.proxy_url()) self.assertIsNone(resp.proxy_url())
resp.source_as2 = 'as2' resp.source_as2 = 'as2'

Wyświetl plik

@ -6,7 +6,7 @@ from unittest.mock import patch
from oauth_dropins.webutil.testutil import requests_response from oauth_dropins.webutil.testutil import requests_response
import common import common
from models import MagicKey from models import Domain
from .test_webmention import REPOST_HTML, REPOST_AS2 from .test_webmention import REPOST_HTML, REPOST_AS2
from . import testutil from . import testutil
@ -15,7 +15,7 @@ class RedirectTest(testutil.TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
MagicKey.get_or_create('foo.com') Domain.get_or_create('foo.com')
def test_redirect(self): def test_redirect(self):
got = self.client.get('/r/https://foo.com/bar?baz=baj&biff') got = self.client.get('/r/https://foo.com/bar?baz=baj&biff')

Wyświetl plik

@ -2,7 +2,7 @@
"""Unit tests for render.py.""" """Unit tests for render.py."""
from oauth_dropins.webutil.util import json_dumps from oauth_dropins.webutil.util import json_dumps
from models import Response from models import Activity
import render import render
from . import testutil from . import testutil
@ -61,31 +61,31 @@ class RenderTest(testutil.TestCase):
resp = self.client.get(f'/render?source={source}&target={target}') resp = self.client.get(f'/render?source={source}&target={target}')
self.assertEqual(400, resp.status_code, resp.get_data(as_text=True)) self.assertEqual(400, resp.status_code, resp.get_data(as_text=True))
# no Response # no Activity
resp = self.client.get('/render?source=abc&target=xyz') resp = self.client.get('/render?source=abc&target=xyz')
self.assertEqual(404, resp.status_code) self.assertEqual(404, resp.status_code)
# no source data # no source data
Response(id='abc xyz').put() Activity(id='abc xyz').put()
resp = self.client.get('/render?source=abc&target=xyz') resp = self.client.get('/render?source=abc&target=xyz')
self.assertEqual(404, resp.status_code) self.assertEqual(404, resp.status_code)
def test_render_as2(self): def test_render_as2(self):
Response(id='abc xyz', source_as2=json_dumps(self.as2)).put() Activity(id='abc xyz', source_as2=json_dumps(self.as2)).put()
resp = self.client.get('/render?source=abc&target=xyz') resp = self.client.get('/render?source=abc&target=xyz')
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(self.html, resp.get_data(as_text=True), self.assert_multiline_equals(self.html, resp.get_data(as_text=True),
ignore_blanks=True) ignore_blanks=True)
def test_render_mf2(self): def test_render_mf2(self):
Response(id='abc xyz', source_mf2=json_dumps(self.mf2)).put() Activity(id='abc xyz', source_mf2=json_dumps(self.mf2)).put()
resp = self.client.get('/render?source=abc&target=xyz') resp = self.client.get('/render?source=abc&target=xyz')
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(self.html, resp.get_data(as_text=True), self.assert_multiline_equals(self.html, resp.get_data(as_text=True),
ignore_blanks=True) ignore_blanks=True)
def test_render_atom(self): def test_render_atom(self):
Response(id='abc xyz', source_atom=self.atom).put() Activity(id='abc xyz', source_atom=self.atom).put()
resp = self.client.get('/render?source=abc&target=xyz') resp = self.client.get('/render?source=abc&target=xyz')
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(self.html, resp.get_data(as_text=True), self.assert_multiline_equals(self.html, resp.get_data(as_text=True),

Wyświetl plik

@ -12,7 +12,7 @@ from oauth_dropins.webutil.testutil import requests_response, UrlopenResult
import requests import requests
import common import common
from models import MagicKey, Response from models import Domain, Activity
from . import testutil from . import testutil
@ -24,7 +24,7 @@ class SalmonTest(testutil.TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.key = MagicKey.get_or_create('alice') self.key = Domain.get_or_create('alice')
def send_slap(self, mock_urlopen, mock_head, mock_get, mock_post, atom_slap): def send_slap(self, mock_urlopen, mock_head, mock_get, mock_post, atom_slap):
# salmon magic key discovery. first host-meta, then webfinger # salmon magic key discovery. first host-meta, then webfinger
@ -89,8 +89,8 @@ class SalmonTest(testutil.TestCase):
allow_redirects=False, allow_redirects=False,
headers={'Accept': '*/*'}) headers={'Accept': '*/*'})
# check stored response # check stored post
resp = Response.get_by_id('https://my/reply http://orig/post') resp = Activity.get_by_id('https://my/reply http://orig/post')
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('ostatus', resp.protocol) self.assertEqual('ostatus', resp.protocol)
@ -124,8 +124,8 @@ class SalmonTest(testutil.TestCase):
allow_redirects=False, allow_redirects=False,
headers={'Accept': '*/*'}) headers={'Accept': '*/*'})
# check stored response # check stored post
resp = Response.get_by_id('https://my/like http://orig/post') resp = Activity.get_by_id('https://my/like http://orig/post')
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('in', resp.direction) self.assertEqual('in', resp.direction)
self.assertEqual('ostatus', resp.protocol) self.assertEqual('ostatus', resp.protocol)

Wyświetl plik

@ -32,7 +32,7 @@ class WebfingerTest(testutil.TestCase):
</a> </a>
</body> </body>
""" """
self.key = models.MagicKey.get_or_create('foo.com') self.key = models.Domain.get_or_create('foo.com')
self.expected_webfinger = { self.expected_webfinger = {
'subject': 'acct:foo.com@foo.com', 'subject': 'acct:foo.com@foo.com',
'aliases': [ 'aliases': [

Wyświetl plik

@ -26,7 +26,7 @@ from common import (
CONTENT_TYPE_HTML, CONTENT_TYPE_HTML,
CONTENT_TYPE_MAGIC_ENVELOPE, CONTENT_TYPE_MAGIC_ENVELOPE,
) )
from models import Follower, MagicKey, Response from models import Follower, Domain, Activity
import webmention import webmention
from . import testutil from . import testutil
@ -68,7 +68,7 @@ REPOST_AS2 = {
class WebmentionTest(testutil.TestCase): class WebmentionTest(testutil.TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.key = MagicKey.get_or_create('a') self.key = Domain.get_or_create('a')
self.orig_html_as2 = requests_response("""\ self.orig_html_as2 = requests_response("""\
<html> <html>
@ -284,7 +284,7 @@ class WebmentionTest(testutil.TestCase):
mock_get.side_effect = ValueError('foo bar') mock_get.side_effect = ValueError('foo bar')
got = self.client.post('/webmention', data={'source': 'bad'}) got = self.client.post('/webmention', data={'source': 'bad'})
self.assertEqual(400, got.status_code) self.assertEqual(400, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
def test_no_source_entry(self, mock_get, mock_post): def test_no_source_entry(self, mock_get, mock_post):
mock_get.return_value = requests_response(""" mock_get.return_value = requests_response("""
@ -299,7 +299,7 @@ class WebmentionTest(testutil.TestCase):
'target': 'https://fed.brid.gy/', 'target': 'https://fed.brid.gy/',
}) })
self.assertEqual(400, got.status_code) self.assertEqual(400, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
mock_get.assert_has_calls((self.req('http://a/post'),)) mock_get.assert_has_calls((self.req('http://a/post'),))
@ -316,7 +316,7 @@ class WebmentionTest(testutil.TestCase):
'target': 'https://fed.brid.gy/', 'target': 'https://fed.brid.gy/',
}) })
self.assertEqual(200, got.status_code) self.assertEqual(200, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
mock_get.assert_has_calls((self.req('http://a/post'),)) mock_get.assert_has_calls((self.req('http://a/post'),))
@ -328,7 +328,7 @@ class WebmentionTest(testutil.TestCase):
got = self.client.post('/webmention', data={'source': 'http://a/post'}) got = self.client.post('/webmention', data={'source': 'http://a/post'})
self.assertEqual(400, got.status_code) self.assertEqual(400, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
def test_source_fetch_fails(self, mock_get, mock_post): def test_source_fetch_fails(self, mock_get, mock_post):
mock_get.side_effect = ( mock_get.side_effect = (
@ -350,7 +350,7 @@ class WebmentionTest(testutil.TestCase):
) )
got = self.client.post('/webmention', data={'source': 'http://a/post'}) got = self.client.post('/webmention', data={'source': 'http://a/post'})
self.assertEqual(502, got.status_code) self.assertEqual(502, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
def test_no_backlink(self, mock_get, mock_post): def test_no_backlink(self, mock_get, mock_post):
mock_get.return_value = requests_response( mock_get.return_value = requests_response(
@ -362,7 +362,7 @@ class WebmentionTest(testutil.TestCase):
'target': 'https://fed.brid.gy/', 'target': 'https://fed.brid.gy/',
}) })
self.assertEqual(400, got.status_code) self.assertEqual(400, got.status_code)
self.assertEqual(0, Response.query().count()) self.assertEqual(0, Activity.query().count())
mock_get.assert_has_calls((self.req('http://a/post'),)) mock_get.assert_has_calls((self.req('http://a/post'),))
@ -405,7 +405,7 @@ class WebmentionTest(testutil.TestCase):
rsa_key = kwargs['auth'].header_signer._rsa._key rsa_key = kwargs['auth'].header_signer._rsa._key
self.assertEqual(self.key.private_pem(), rsa_key.exportKey()) self.assertEqual(self.key.private_pem(), rsa_key.exportKey())
resp = Response.get_by_id('http://a/reply http://orig/as2') resp = Activity.get_by_id('http://a/reply http://orig/as2')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -419,7 +419,7 @@ class WebmentionTest(testutil.TestCase):
# self.assertEqual(['abc xyz'], resp.responses) # self.assertEqual(['abc xyz'], resp.responses)
def test_activitypub_update_reply(self, mock_get, mock_post): def test_activitypub_update_reply(self, mock_get, mock_post):
Response(id='http://a/reply http://orig/as2', status='complete').put() Activity(id='http://a/reply http://orig/as2', status='complete').put()
mock_get.side_effect = self.activitypub_gets mock_get.side_effect = self.activitypub_gets
mock_post.return_value = requests_response('abc xyz') mock_post.return_value = requests_response('abc xyz')
@ -436,7 +436,7 @@ class WebmentionTest(testutil.TestCase):
def test_activitypub_skip_update_if_content_unchanged(self, mock_get, mock_post): def test_activitypub_skip_update_if_content_unchanged(self, mock_get, mock_post):
"""https://github.com/snarfed/bridgy-fed/issues/78""" """https://github.com/snarfed/bridgy-fed/issues/78"""
Response(id='http://a/reply http://orig/as2', status='complete', Activity(id='http://a/reply http://orig/as2', status='complete',
source_mf2=json_dumps(self.reply_mf2)).put() source_mf2=json_dumps(self.reply_mf2)).put()
mock_get.side_effect = self.activitypub_gets mock_get.side_effect = self.activitypub_gets
@ -508,7 +508,7 @@ class WebmentionTest(testutil.TestCase):
rsa_key = kwargs['auth'].header_signer._rsa._key rsa_key = kwargs['auth'].header_signer._rsa._key
self.assertEqual(self.key.private_pem(), rsa_key.exportKey()) self.assertEqual(self.key.private_pem(), rsa_key.exportKey())
resp = Response.get_by_id('http://a/repost http://orig/as2') resp = Activity.get_by_id('http://a/repost http://orig/as2')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -605,12 +605,12 @@ class WebmentionTest(testutil.TestCase):
mock_get.side_effect = [self.create, self.actor] mock_get.side_effect = [self.create, self.actor]
mock_post.return_value = requests_response('abc xyz') mock_post.return_value = requests_response('abc xyz')
Response(id='http://orig/post https://skipped/inbox', domain='orig', Activity(id='http://orig/post https://skipped/inbox', domain='orig',
status='complete', source_mf2=json_dumps(self.create_mf2)).put() status='complete', source_mf2=json_dumps(self.create_mf2)).put()
different_create_mf2 = copy.deepcopy(self.create_mf2) different_create_mf2 = copy.deepcopy(self.create_mf2)
different_create_mf2['items'][0]['properties']['content'][0]['value'] += ' different' different_create_mf2['items'][0]['properties']['content'][0]['value'] += ' different'
Response(id='http://orig/post https://updated/inbox', domain='orig', Activity(id='http://orig/post https://updated/inbox', domain='orig',
status='complete', direction='out', protocol='activitypub', status='complete', direction='out', protocol='activitypub',
source_mf2=json_dumps(different_create_mf2)).put() source_mf2=json_dumps(different_create_mf2)).put()
@ -672,7 +672,7 @@ class WebmentionTest(testutil.TestCase):
self.update_as2 if inbox == 'https://updated/inbox' else self.create_as2, self.update_as2 if inbox == 'https://updated/inbox' else self.create_as2,
json_loads(call[1]['data'])) json_loads(call[1]['data']))
resp = Response.get_by_id('http://orig/post %s' % inbox) resp = Activity.get_by_id('http://orig/post %s' % inbox)
self.assertEqual('orig', resp.domain) self.assertEqual('orig', resp.domain)
self.assertEqual('out', resp.direction, inbox) self.assertEqual('out', resp.direction, inbox)
self.assertEqual('activitypub', resp.protocol, inbox) self.assertEqual('activitypub', resp.protocol, inbox)
@ -733,7 +733,7 @@ class WebmentionTest(testutil.TestCase):
rsa_key = kwargs['auth'].header_signer._rsa._key rsa_key = kwargs['auth'].header_signer._rsa._key
self.assertEqual(self.key.private_pem(), rsa_key.exportKey()) self.assertEqual(self.key.private_pem(), rsa_key.exportKey())
resp = Response.get_by_id('http://a/follow http://followee/') resp = Activity.get_by_id('http://a/follow http://followee/')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -776,7 +776,7 @@ class WebmentionTest(testutil.TestCase):
rsa_key = kwargs['auth'].header_signer._rsa._key rsa_key = kwargs['auth'].header_signer._rsa._key
self.assertEqual(self.key.private_pem(), rsa_key.exportKey()) self.assertEqual(self.key.private_pem(), rsa_key.exportKey())
resp = Response.get_by_id('http://a/follow http://followee/') resp = Activity.get_by_id('http://a/follow http://followee/')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('activitypub', resp.protocol) self.assertEqual('activitypub', resp.protocol)
@ -821,7 +821,7 @@ class WebmentionTest(testutil.TestCase):
<a href="http://localhost/"></a>""", <a href="http://localhost/"></a>""",
entry.content[0]['value']) entry.content[0]['value'])
resp = Response.get_by_id('http://a/reply http://orig/post') resp = Activity.get_by_id('http://a/reply http://orig/post')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('ostatus', resp.protocol) self.assertEqual('ostatus', resp.protocol)
@ -855,7 +855,7 @@ class WebmentionTest(testutil.TestCase):
}, entry['links']) }, entry['links'])
self.assertEqual('http://orig/post', entry['activity_object']) self.assertEqual('http://orig/post', entry['activity_object'])
resp = Response.get_by_id('http://a/like http://orig/post') resp = Activity.get_by_id('http://a/like http://orig/post')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('ostatus', resp.protocol) self.assertEqual('ostatus', resp.protocol)
@ -907,7 +907,7 @@ class WebmentionTest(testutil.TestCase):
self.assertIn('Target post http://orig/url has no Atom link', self.assertIn('Target post http://orig/url has no Atom link',
got.get_data(as_text=True)) got.get_data(as_text=True))
resp = Response.get_by_id('http://a/reply http://orig/url') resp = Activity.get_by_id('http://a/reply http://orig/url')
self.assertEqual('a', resp.domain) self.assertEqual('a', resp.domain)
self.assertEqual('out', resp.direction) self.assertEqual('out', resp.direction)
self.assertEqual('ostatus', resp.protocol) self.assertEqual('ostatus', resp.protocol)

Wyświetl plik

@ -59,7 +59,7 @@ class User(flask_util.XrdOrJrd):
error(f"didn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {resp.url}") error(f"didn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {resp.url}")
logger.info(f'Generating WebFinger data for {domain}') logger.info(f'Generating WebFinger data for {domain}')
key = models.MagicKey.get_or_create(domain) key = models.Domain.get_or_create(domain)
props = hcard.get('properties', {}) props = hcard.get('properties', {})
urls = util.dedupe_urls(props.get('url', []) + [resp.url]) urls = util.dedupe_urls(props.get('url', []) + [resp.url])
canonical_url = urls[0] canonical_url = urls[0]

Wyświetl plik

@ -25,7 +25,7 @@ from werkzeug.exceptions import BadGateway
import activitypub import activitypub
from app import app from app import app
import common import common
from models import Follower, MagicKey, Response from models import Follower, Domain, Activity
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -91,7 +91,7 @@ class Webmention(View):
if not targets: if not targets:
return None return None
key = MagicKey.get_or_create(self.source_domain) key = Domain.get_or_create(self.source_domain)
error = None error = None
last_success = None last_success = None
@ -160,7 +160,7 @@ class Webmention(View):
def _activitypub_targets(self): def _activitypub_targets(self):
""" """
Returns: list of (Response, string inbox URL) Returns: list of (Activity, string inbox URL)
""" """
# if there's in-reply-to, like-of, or repost-of, they're the targets. # if there's in-reply-to, like-of, or repost-of, they're the targets.
# otherwise, it's all followers' inboxes. # otherwise, it's all followers' inboxes.
@ -178,7 +178,7 @@ class Webmention(View):
inboxes.add(actor.get('endpoints', {}).get('sharedInbox') or inboxes.add(actor.get('endpoints', {}).get('sharedInbox') or
actor.get('publicInbox')or actor.get('publicInbox')or
actor.get('inbox')) actor.get('inbox'))
return [(Response.get_or_create( return [(Activity.get_or_create(
source=self.source_url, target=inbox, domain=self.source_domain, source=self.source_url, target=inbox, domain=self.source_domain,
direction='out', protocol='activitypub', direction='out', protocol='activitypub',
source_mf2=json_dumps(self.source_mf2)), source_mf2=json_dumps(self.source_mf2)),
@ -200,7 +200,7 @@ class Webmention(View):
raise raise
target_url = self.target_resp.url or target target_url = self.target_resp.url or target
resp = Response.get_or_create( resp = Activity.get_or_create(
source=self.source_url, target=target_url, domain=self.source_domain, source=self.source_url, target=target_url, domain=self.source_domain,
direction='out', protocol='activitypub', direction='out', protocol='activitypub',
source_mf2=json_dumps(self.source_mf2)) source_mf2=json_dumps(self.source_mf2))
@ -266,7 +266,7 @@ class Webmention(View):
raise raise
finally: finally:
if status: if status:
Response(source=self.source_url, target=target, status=status, Activity(source=self.source_url, target=target, status=status,
domain=self.source_domain, direction='out', domain=self.source_domain, direction='out',
protocol = 'ostatus', protocol = 'ostatus',
source_mf2=json_dumps(self.source_mf2)).put() source_mf2=json_dumps(self.source_mf2)).put()
@ -351,7 +351,7 @@ class Webmention(View):
# sign reply and wrap in magic envelope # sign reply and wrap in magic envelope
domain = urllib.parse.urlparse(self.source_url).netloc domain = urllib.parse.urlparse(self.source_url).netloc
key = MagicKey.get_or_create(domain) key = Domain.get_or_create(domain)
logger.info(f'Using key for {domain}: {key}') logger.info(f'Using key for {domain}: {key}')
magic_envelope = magicsigs.magic_envelope( magic_envelope = magicsigs.magic_envelope(
entry, common.CONTENT_TYPE_ATOM, key).decode() entry, common.CONTENT_TYPE_ATOM, key).decode()