diff --git a/activitypub.py b/activitypub.py index 0a5bf2a..78251c4 100644 --- a/activitypub.py +++ b/activitypub.py @@ -17,7 +17,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads from app import app, cache import common from common import redirect_unwrap, redirect_wrap -from models import Follower, MagicKey +from models import Follower, Domain from httpsig.requests_auth import HTTPSignatureAuth 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://github.com/tootsuite/mastodon/issues/4906#issuecomment-328844846 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, algorithm='rsa-sha256', sign_header='signature', headers=('Date', 'Digest', 'Host')) @@ -94,7 +94,7 @@ def actor(domain): if not hcard: 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( as2.from_as1(microformats2.json_to_object(hcard)), key=key) obj.update({ diff --git a/common.py b/common.py index 332c5dd..4570e00 100644 --- a/common.py +++ b/common.py @@ -14,7 +14,7 @@ from oauth_dropins.webutil.flask_util import error import requests from werkzeug.exceptions import BadGateway -from models import Response +from models import Activity logger = logging.getLogger(__name__) @@ -144,11 +144,11 @@ def content_type(resp): 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. Args: 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) @@ -186,7 +186,7 @@ def send_webmentions(activity_wrapped, proxy=None, **response_props): if not targets: 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 for target in targets: 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}') continue - response = Response(source=source, target=target, direction='in', - domain=domain, **response_props) - response.put() - wm_source = (response.proxy_url() + activity = Activity(source=source, target=target, direction='in', + domain=domain, **activity_props) + activity.put() + wm_source = (activity.proxy_url() if verb in ('follow', 'like', 'share') or proxy else source) 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 if endpoint: webmention.send(endpoint, wm_source, target) - response.status = 'complete' + activity.status = 'complete' logger.info('Success!') else: - response.status = 'ignored' + activity.status = 'ignored' logger.info('Ignoring.') except BaseException as e: errors.append(util.interpret_http_exception(e)) - response.put() + activity.put() if 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 target: dict, AS2 object, optional. The target of activity's inReplyTo or 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. """ type = activity.get('type') diff --git a/models.py b/models.py index c973cbc..bba9345 100644 --- a/models.py +++ b/models.py @@ -11,7 +11,7 @@ from oauth_dropins.webutil.models import StringIdModel logger = logging.getLogger(__name__) -class MagicKey(StringIdModel): +class Domain(StringIdModel): """Stores a user's public/private key pair used for Magic Signatures. The key name is the domain. @@ -28,17 +28,21 @@ class MagicKey(StringIdModel): public_exponent = ndb.StringProperty(required=True) private_exponent = ndb.StringProperty(required=True) + @classmethod + def _get_kind(cls): + return 'MagicKey' + @staticmethod @ndb.transactional() def get_or_create(domain): - """Loads and returns a MagicKey. Creates it if necessary.""" - key = MagicKey.get_by_id(domain) + """Loads and returns a Domain. Creates it if necessary.""" + key = Domain.get_by_id(domain) if not key: # this uses urandom(), and does nontrivial math, so it can take a # while depending on the amount of randomness available. 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) key.put() @@ -62,7 +66,7 @@ class MagicKey(StringIdModel): return rsa.exportKey(format='PEM') -class Response(StringIdModel): +class Activity(StringIdModel): """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'. @@ -86,16 +90,20 @@ class Response(StringIdModel): created = ndb.DateTimeProperty(auto_now_add=True) updated = ndb.DateTimeProperty(auto_now=True) + @classmethod + def _get_kind(cls): + return 'Response' + def __init__(self, source=None, target=None, **kwargs): if source and target: assert 'id' not in kwargs kwargs['id'] = self._id(source, target) - logger.info(f"Response id (source target): {kwargs['id']}") - super(Response, self).__init__(**kwargs) + logger.info(f"Activity id (source target): {kwargs['id']}") + super(Activity, self).__init__(**kwargs) @classmethod 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) def source(self): @@ -105,7 +113,7 @@ class Response(StringIdModel): return self.key.id().split()[1] 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: source, target = self.key.id().split(' ') return f'{request.host_url}render?' + urllib.parse.urlencode({ diff --git a/pages.py b/pages.py index 87a8c58..155fa71 100644 --- a/pages.py +++ b/pages.py @@ -1,4 +1,4 @@ -"""Render recent responses and logs.""" +"""UI pages.""" import calendar import datetime from itertools import islice @@ -12,7 +12,7 @@ from oauth_dropins.webutil.flask_util import error from app import app, cache import common -from models import Follower, MagicKey, Response +from models import Follower, Domain, Activity PAGE_SIZE = 20 FOLLOWERS_UI_LIMIT = 999 @@ -28,14 +28,14 @@ def front_page(): @app.get(f'/user/') @app.get(f'/responses/') # deprecated 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 - query = Response.query( - Response.status.IN(('new', 'complete', 'error')), - Response.domain == domain, + query = Activity.query( + Activity.status.IN(('new', 'complete', 'error')), + Activity.domain == domain, ) - responses, before, after = fetch_page(query, Response) + activities, before, after = fetch_page(query, Activity) followers = Follower.query(Follower.dest == domain)\ .count(limit=FOLLOWERS_UI_LIMIT) @@ -54,7 +54,10 @@ def user(domain): @app.get(f'/user//followers') 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 query = Follower.query( @@ -79,7 +82,7 @@ def followers(domain): @app.get(f'/user//following') 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 query = Follower.query( @@ -105,9 +108,9 @@ def following(domain): @app.get('/recent') @app.get('/responses') # deprecated def recent(): - """Renders recent Responses, with links to logs.""" - query = Response.query(Response.status.IN(('new', 'complete', 'error'))) - responses, before, after = fetch_page(query, Response) + """Renders recent activities, with links to logs.""" + query = Activity.query(Activity.status.IN(('new', 'complete', 'error'))) + activities, before, after = fetch_page(query, Activity) return render_template( 'recent.html', util=util, @@ -186,8 +189,8 @@ def fetch_page(query, model_class): def stats(): return render_template( 'stats.html', - users=KindStat.query(KindStat.kind_name == 'MagicKey').get().count, - responses=KindStat.query(KindStat.kind_name == 'Response').get().count, + users=KindStat.query(KindStat.kind_name == 'Domain').get().count, + activities=KindStat.query(KindStat.kind_name == 'Activity').get().count, followers=KindStat.query(KindStat.kind_name == 'Follower').get().count, ) diff --git a/redirect.py b/redirect.py index 7607bb5..7bfd4c5 100644 --- a/redirect.py +++ b/redirect.py @@ -23,7 +23,7 @@ from werkzeug.exceptions import abort from app import app, cache import common -from models import MagicKey +from models import Domain logger = logging.getLogger(__name__) @@ -51,8 +51,8 @@ def redir(to): util.domain_from_link(to, minimize=False), urllib.parse.urlparse(to).hostname)) for domain in domains: - if domain and MagicKey.get_by_id(domain): - logger.info(f'Found MagicKey for domain {domain}') + if domain and Domain.get_by_id(domain): + logger.info(f'Found Domain for domain {domain}') break else: logger.info(f'No user found for any of {domains}; returning 404') diff --git a/render.py b/render.py index a5c7a38..b13038e 100644 --- a/render.py +++ b/render.py @@ -1,5 +1,5 @@ # coding=utf-8 -"""Renders mf2 proxy pages based on stored Responses.""" +"""Renders mf2 proxy pages based on stored Activity entities.""" import datetime from flask import request @@ -10,7 +10,7 @@ from oauth_dropins.webutil.util import json_loads from app import app, cache import common -from models import Response +from models import Activity CACHE_TIME = datetime.timedelta(minutes=15) @@ -18,23 +18,23 @@ CACHE_TIME = datetime.timedelta(minutes=15) @app.get('/render') @flask_util.cached(cache, CACHE_TIME) 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') target = flask_util.get_required_param('target') id = f'{source} {target}' - resp = Response.get_by_id(id) - if not resp: - error(f'No stored response for {id}', status=404) + activity = Activity.get_by_id(id) + if not activity: + error(f'No stored activity for {id}', status=404) - if resp.source_mf2: - as1 = microformats2.json_to_object(json_loads(resp.source_mf2)) - elif resp.source_as2: - as1 = as2.to_as1(json_loads(resp.source_as2)) - elif resp.source_atom: - as1 = atom.atom_to_activity(resp.source_atom) + if activity.source_mf2: + as1 = microformats2.json_to_object(json_loads(activity.source_mf2)) + elif activity.source_as2: + as1 = as2.to_as1(json_loads(activity.source_as2)) + elif activity.source_atom: + as1 = atom.atom_to_activity(activity.source_atom) 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 # browsers but not for webmention receivers (hopefully). diff --git a/templates/activities.html b/templates/activities.html new file mode 100644 index 0000000..a2403e5 --- /dev/null +++ b/templates/activities.html @@ -0,0 +1,23 @@ + + + +
+ +{% for a in activities %} +
+
{{ util.pretty_link(a.source())|safe }}
+
{{ util.pretty_link(a.target())|safe }}
+
{{ a.protocol }}
+
{{ a.status }}
+ +
+{% else %} +
None
+{% endfor %} + + +{% include "paging.html" %} diff --git a/templates/paging.html b/templates/paging.html index c730720..767a26a 100644 --- a/templates/paging.html +++ b/templates/paging.html @@ -1,13 +1,13 @@
{% if after %} - ← Newer + ← Newer {% endif %}
{% if before %} - Older → + Older → {% endif %}
diff --git a/templates/recent.html b/templates/recent.html index 88486a4..52d1ee6 100644 --- a/templates/recent.html +++ b/templates/recent.html @@ -6,6 +6,6 @@

Recent activity

-{% include "responses.html" %} +{% include "activities.html" %} {% endblock %} diff --git a/templates/responses.html b/templates/responses.html deleted file mode 100644 index d72115d..0000000 --- a/templates/responses.html +++ /dev/null @@ -1,23 +0,0 @@ - - - -
- -{% for r in responses %} -
-
{{ util.pretty_link(r.source())|safe }}
-
{{ util.pretty_link(r.target())|safe }}
-
{{ r.protocol }}
-
{{ r.status }}
- -
-{% else %} -
None
-{% endfor %} - - -{% include "paging.html" %} diff --git a/templates/stats.html b/templates/stats.html index 3fd079c..7b3f613 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -12,7 +12,7 @@
  • Launched Octover 17, 2022
  • {{ users }} users
  • {{ followers }} fediverse followers
  • -
  • {{ responses }} activities handled
  • +
  • {{ activities }} activities handled
  • diff --git a/templates/user.html b/templates/user.html index 261ce6d..8a7e2ea 100644 --- a/templates/user.html +++ b/templates/user.html @@ -16,6 +16,6 @@

    Recent activity

    -{% include "responses.html" %} +{% include "activities.html" %} {% endblock %} diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index ae627a5..65cb430 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -14,7 +14,7 @@ from urllib3.exceptions import ReadTimeoutError import activitypub import common -from models import Follower, MagicKey, Response +from models import Follower, Domain, Activity from . import testutil REPLY_OBJECT = { @@ -173,7 +173,7 @@ class ActivityPubTest(testutil.TestCase): 'publicKey': { 'id': '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) @@ -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('in', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -257,7 +257,7 @@ class ActivityPubTest(testutil.TestCase): self.assert_req(mock_head, 'http://this', allow_redirects=True) mock_get.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): 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('in', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -319,7 +319,7 @@ class ActivityPubTest(testutil.TestCase): 'target': 'http://orig/post', }, 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('in', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -358,7 +358,7 @@ class ActivityPubTest(testutil.TestCase): 'target': 'https://www.realize.be/', }, 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('in', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -443,7 +443,7 @@ class ActivityPubTest(testutil.TestCase): got = self.client.post('/foo.com/inbox', json=LIKE) 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('in', resp.direction) self.assertEqual('activitypub', resp.protocol) diff --git a/tests/test_models.py b/tests/test_models.py index 14f42c4..9179421 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,22 +1,22 @@ # coding=utf-8 """Unit tests for models.py.""" from app import app -from models import MagicKey, Response +from models import Domain, Activity from . import testutil -class MagicKeyTest(testutil.TestCase): +class DomainTest(testutil.TestCase): def setUp(self): - super(MagicKeyTest, self).setUp() - self.key = MagicKey.get_or_create('y.z') + super(DomainTest, self).setUp() + self.key = Domain.get_or_create('y.z') def test_magic_key_get_or_create(self): assert self.key.mod assert self.key.public_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) def test_href(self): @@ -36,25 +36,25 @@ class MagicKeyTest(testutil.TestCase): self.assertTrue(pem.decode().endswith('-----END RSA PRIVATE KEY-----'), pem) -class ResponseTest(testutil.TestCase): +class ActivityTest(testutil.TestCase): def test_constructor(self): - resp = Response('abc', 'xyz') + resp = Activity('abc', 'xyz') 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()) 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()) - 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()) def test_proxy_url(self): with app.test_request_context('/'): - resp = Response.get_or_create('abc', 'xyz') + resp = Activity.get_or_create('abc', 'xyz') self.assertIsNone(resp.proxy_url()) resp.source_as2 = 'as2' diff --git a/tests/test_redirect.py b/tests/test_redirect.py index 11623dd..df68648 100644 --- a/tests/test_redirect.py +++ b/tests/test_redirect.py @@ -6,7 +6,7 @@ from unittest.mock import patch from oauth_dropins.webutil.testutil import requests_response import common -from models import MagicKey +from models import Domain from .test_webmention import REPOST_HTML, REPOST_AS2 from . import testutil @@ -15,7 +15,7 @@ class RedirectTest(testutil.TestCase): def setUp(self): super().setUp() - MagicKey.get_or_create('foo.com') + Domain.get_or_create('foo.com') def test_redirect(self): got = self.client.get('/r/https://foo.com/bar?baz=baj&biff') diff --git a/tests/test_render.py b/tests/test_render.py index 3cc8919..f0fed2c 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -2,7 +2,7 @@ """Unit tests for render.py.""" from oauth_dropins.webutil.util import json_dumps -from models import Response +from models import Activity import render from . import testutil @@ -61,31 +61,31 @@ class RenderTest(testutil.TestCase): resp = self.client.get(f'/render?source={source}&target={target}') 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') self.assertEqual(404, resp.status_code) # no source data - Response(id='abc xyz').put() + Activity(id='abc xyz').put() resp = self.client.get('/render?source=abc&target=xyz') self.assertEqual(404, resp.status_code) 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') self.assertEqual(200, resp.status_code) self.assert_multiline_equals(self.html, resp.get_data(as_text=True), ignore_blanks=True) 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') self.assertEqual(200, resp.status_code) self.assert_multiline_equals(self.html, resp.get_data(as_text=True), ignore_blanks=True) 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') self.assertEqual(200, resp.status_code) self.assert_multiline_equals(self.html, resp.get_data(as_text=True), diff --git a/tests/test_salmon.py b/tests/test_salmon.py index 575c7b6..97f5d53 100644 --- a/tests/test_salmon.py +++ b/tests/test_salmon.py @@ -12,7 +12,7 @@ from oauth_dropins.webutil.testutil import requests_response, UrlopenResult import requests import common -from models import MagicKey, Response +from models import Domain, Activity from . import testutil @@ -24,7 +24,7 @@ class SalmonTest(testutil.TestCase): def setUp(self): 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): # salmon magic key discovery. first host-meta, then webfinger @@ -89,8 +89,8 @@ class SalmonTest(testutil.TestCase): allow_redirects=False, headers={'Accept': '*/*'}) - # check stored response - resp = Response.get_by_id('https://my/reply http://orig/post') + # check stored post + resp = Activity.get_by_id('https://my/reply http://orig/post') self.assertEqual('orig', resp.domain) self.assertEqual('in', resp.direction) self.assertEqual('ostatus', resp.protocol) @@ -124,8 +124,8 @@ class SalmonTest(testutil.TestCase): allow_redirects=False, headers={'Accept': '*/*'}) - # check stored response - resp = Response.get_by_id('https://my/like http://orig/post') + # check stored post + resp = Activity.get_by_id('https://my/like http://orig/post') self.assertEqual('orig', resp.domain) self.assertEqual('in', resp.direction) self.assertEqual('ostatus', resp.protocol) diff --git a/tests/test_webfinger.py b/tests/test_webfinger.py index f284905..e8b0574 100644 --- a/tests/test_webfinger.py +++ b/tests/test_webfinger.py @@ -32,7 +32,7 @@ class WebfingerTest(testutil.TestCase): """ - self.key = models.MagicKey.get_or_create('foo.com') + self.key = models.Domain.get_or_create('foo.com') self.expected_webfinger = { 'subject': 'acct:foo.com@foo.com', 'aliases': [ diff --git a/tests/test_webmention.py b/tests/test_webmention.py index c97727f..a8ff5af 100644 --- a/tests/test_webmention.py +++ b/tests/test_webmention.py @@ -26,7 +26,7 @@ from common import ( CONTENT_TYPE_HTML, CONTENT_TYPE_MAGIC_ENVELOPE, ) -from models import Follower, MagicKey, Response +from models import Follower, Domain, Activity import webmention from . import testutil @@ -68,7 +68,7 @@ REPOST_AS2 = { class WebmentionTest(testutil.TestCase): def setUp(self): super().setUp() - self.key = MagicKey.get_or_create('a') + self.key = Domain.get_or_create('a') self.orig_html_as2 = requests_response("""\ @@ -284,7 +284,7 @@ class WebmentionTest(testutil.TestCase): mock_get.side_effect = ValueError('foo bar') got = self.client.post('/webmention', data={'source': 'bad'}) 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): mock_get.return_value = requests_response(""" @@ -299,7 +299,7 @@ class WebmentionTest(testutil.TestCase): 'target': 'https://fed.brid.gy/', }) 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'),)) @@ -316,7 +316,7 @@ class WebmentionTest(testutil.TestCase): 'target': 'https://fed.brid.gy/', }) 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'),)) @@ -328,7 +328,7 @@ class WebmentionTest(testutil.TestCase): got = self.client.post('/webmention', data={'source': 'http://a/post'}) 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): mock_get.side_effect = ( @@ -350,7 +350,7 @@ class WebmentionTest(testutil.TestCase): ) got = self.client.post('/webmention', data={'source': 'http://a/post'}) 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): mock_get.return_value = requests_response( @@ -362,7 +362,7 @@ class WebmentionTest(testutil.TestCase): 'target': 'https://fed.brid.gy/', }) 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'),)) @@ -405,7 +405,7 @@ class WebmentionTest(testutil.TestCase): rsa_key = kwargs['auth'].header_signer._rsa._key 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('out', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -419,7 +419,7 @@ class WebmentionTest(testutil.TestCase): # self.assertEqual(['abc xyz'], resp.responses) 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_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): """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() mock_get.side_effect = self.activitypub_gets @@ -508,7 +508,7 @@ class WebmentionTest(testutil.TestCase): rsa_key = kwargs['auth'].header_signer._rsa._key 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('out', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -605,12 +605,12 @@ class WebmentionTest(testutil.TestCase): mock_get.side_effect = [self.create, self.actor] 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() different_create_mf2 = copy.deepcopy(self.create_mf2) 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', 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, 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('out', resp.direction, inbox) self.assertEqual('activitypub', resp.protocol, inbox) @@ -733,7 +733,7 @@ class WebmentionTest(testutil.TestCase): rsa_key = kwargs['auth'].header_signer._rsa._key 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('out', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -776,7 +776,7 @@ class WebmentionTest(testutil.TestCase): rsa_key = kwargs['auth'].header_signer._rsa._key 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('out', resp.direction) self.assertEqual('activitypub', resp.protocol) @@ -821,7 +821,7 @@ class WebmentionTest(testutil.TestCase): """, 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('out', resp.direction) self.assertEqual('ostatus', resp.protocol) @@ -855,7 +855,7 @@ class WebmentionTest(testutil.TestCase): }, entry['links']) 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('out', resp.direction) self.assertEqual('ostatus', resp.protocol) @@ -907,7 +907,7 @@ class WebmentionTest(testutil.TestCase): self.assertIn('Target post http://orig/url has no Atom link', 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('out', resp.direction) self.assertEqual('ostatus', resp.protocol) diff --git a/webfinger.py b/webfinger.py index 4c122f6..72853b3 100644 --- a/webfinger.py +++ b/webfinger.py @@ -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}") 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', {}) urls = util.dedupe_urls(props.get('url', []) + [resp.url]) canonical_url = urls[0] diff --git a/webmention.py b/webmention.py index db761be..4ab9c50 100644 --- a/webmention.py +++ b/webmention.py @@ -25,7 +25,7 @@ from werkzeug.exceptions import BadGateway import activitypub from app import app import common -from models import Follower, MagicKey, Response +from models import Follower, Domain, Activity logger = logging.getLogger(__name__) @@ -91,7 +91,7 @@ class Webmention(View): if not targets: return None - key = MagicKey.get_or_create(self.source_domain) + key = Domain.get_or_create(self.source_domain) error = None last_success = None @@ -160,7 +160,7 @@ class Webmention(View): 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. # otherwise, it's all followers' inboxes. @@ -178,7 +178,7 @@ class Webmention(View): inboxes.add(actor.get('endpoints', {}).get('sharedInbox') or actor.get('publicInbox')or actor.get('inbox')) - return [(Response.get_or_create( + return [(Activity.get_or_create( source=self.source_url, target=inbox, domain=self.source_domain, direction='out', protocol='activitypub', source_mf2=json_dumps(self.source_mf2)), @@ -200,7 +200,7 @@ class Webmention(View): raise 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, direction='out', protocol='activitypub', source_mf2=json_dumps(self.source_mf2)) @@ -266,7 +266,7 @@ class Webmention(View): raise finally: 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', protocol = 'ostatus', source_mf2=json_dumps(self.source_mf2)).put() @@ -351,7 +351,7 @@ class Webmention(View): # sign reply and wrap in magic envelope 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}') magic_envelope = magicsigs.magic_envelope( entry, common.CONTENT_TYPE_ATOM, key).decode()