follow UI: synthesize Follow activity id, store an Activity, link address, form bug fix

pull/356/head
Ryan Barrett 2023-01-07 18:00:19 -08:00
rodzic c50f0e0106
commit 91c4200bb3
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
4 zmienionych plików z 34 dodań i 17 usunięć

Wyświetl plik

@ -13,11 +13,12 @@ from oauth_dropins import indieauth
from oauth_dropins.webutil import flask_util, util
from oauth_dropins.webutil.flask_util import error, flash
from oauth_dropins.webutil import util
from oauth_dropins.webutil.testutil import NOW
from oauth_dropins.webutil.util import json_dumps, json_loads
from app import app
import common
from models import Follower, User
from models import Activity, Follower, User
logger = logging.getLogger(__name__)
@ -120,9 +121,6 @@ class FollowCallback(indieauth.Callback):
if not User.get_by_id(domain):
error(f'No user for domain {domain}')
# addr = state.get('address')
# if not addr:
# error(f'state missing address field')
addr = state
assert addr
webfinger = fetch_webfinger(addr)
@ -146,18 +144,25 @@ class FollowCallback(indieauth.Callback):
flash(f"AS2 profile {as2_url} missing id or inbox")
return redirect(f'/user/{domain}/following')
common.signed_post(inbox, data={
timestamp = NOW.replace(microsecond=0, tzinfo=None).isoformat()
follow_as2 = {
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Follow',
'id': 'TODO',
'id': common.host_url(f'/user/{domain}/following#{timestamp}-{addr}'),
'object': id,
'actor': common.host_url(domain),
'to': [as2.PUBLIC_AUDIENCE],
})
}
common.signed_post(inbox, data=follow_as2)
Follower.get_or_create(dest=id, src=domain, status='active',
last_follow=json_dumps({}))
flash(f'Followed {addr}.')
Activity.get_or_create(source='UI', target=id, domain=[domain],
direction='out', protocol='activitypub', status='complete',
source_as2=json_dumps(follow_as2, sort_keys=True))
link = util.pretty_link(obj.get('url') or id, text=addr)
flash(f'Followed {link}.')
return redirect(f'/user/{domain}/following')
@ -165,4 +170,5 @@ app.add_url_rule('/follow/start',
view_func=FollowStart.as_view('follow_start', '/follow/callback'),
methods=['POST'])
app.add_url_rule('/follow/callback',
view_func=FollowCallback.as_view('follow_callback', 'unused'))
view_func=FollowCallback.as_view('follow_callback', 'unused'),
methods=['GET'])

Wyświetl plik

@ -333,8 +333,8 @@ class Follower(StringIdModel):
src = ndb.StringProperty()
dest = ndb.StringProperty()
# most recent AP Follow activity (JSON). must have a composite actor object
# with an inbox, publicInbox, or sharedInbox!
# Most recent AP Follow activity (JSON). If this is an inbound follow, must
# have a composite actor object with an inbox, publicInbox, or sharedInbox.
last_follow = ndb.TextProperty()
status = ndb.StringProperty(choices=STATUSES, default='active')

Wyświetl plik

@ -9,7 +9,7 @@
<div class="row big">Following</div>
<div class="row">
<form method="post" action="/follow">
<form method="post" action="/follow/start">
<p>
<label for="follower-address">Add a follower (requires <a href="https://indieauth.net/">IndieAuth</a>):</label>
<input id="follower-address" name="address" type="text" required

Wyświetl plik

@ -10,7 +10,7 @@ from oauth_dropins.webutil.testutil import requests_response
from oauth_dropins.webutil.util import json_dumps, json_loads
import common
from models import Follower, User
from models import Activity, Follower, User
from . import testutil
WEBFINGER = requests_response({
@ -119,6 +119,7 @@ class AddFollowerTest(testutil.TestCase):
self.as2_resp({
'type': 'Person',
'id': 'https://bar/id',
'url': 'https://bar/url',
'inbox': 'http://bar/inbox',
}),
)
@ -133,7 +134,7 @@ class AddFollowerTest(testutil.TestCase):
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/snarfed.org/following',resp.headers['Location'])
self.assertEqual(['Followed @foo@bar.'], get_flashed_messages())
self.assertEqual(['Followed <a href="https://bar/url">@foo@bar</a>.'], get_flashed_messages())
mock_get.assert_has_calls((
self.req('https://bar/.well-known/webfinger?resource=acct:foo@bar'),
@ -150,19 +151,29 @@ class AddFollowerTest(testutil.TestCase):
))
inbox_args, inbox_kwargs = mock_post.call_args_list[-1]
self.assertEqual(('http://bar/inbox',), inbox_args)
self.assert_equals({
expected_follow = {
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Follow',
'id': 'TODO',
'id': 'http://localhost/user/snarfed.org/following#2022-01-02T03:04:05-@foo@bar',
'actor': 'http://localhost/snarfed.org',
'object': 'https://bar/id',
'to': [as2.PUBLIC_AUDIENCE],
}, json_loads(inbox_kwargs['data']))
}
self.assert_equals(expected_follow, json_loads(inbox_kwargs['data']))
followers = Follower.query().fetch()
self.assertEqual(1, len(followers))
self.assertEqual('https://bar/id snarfed.org', followers[0].key.id())
activities = Activity.query().fetch()
self.assert_entities_equal(
[Activity(id='UI https://bar/id', domain=['snarfed.org'],
status='complete', protocol='activitypub', direction='out',
source_as2=json_dumps(expected_follow, sort_keys=True))],
activities,
ignore=['created', 'updated'])
def test_callback_missing_user(self, mock_get, mock_post):
mock_post.return_value = requests_response('me=https://snarfed.org')