AP users: add User.name() and label_id(), ActivityPub.address computed property

for #512
circle-datastore-transactions
Ryan Barrett 2023-06-01 21:37:58 -07:00
rodzic 17ed24b6f5
commit ca64793fff
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
7 zmienionych plików z 60 dodań i 9 usunięć

Wyświetl plik

@ -6,6 +6,7 @@ import logging
from urllib.parse import quote_plus
from flask import abort, g, request
from google.cloud import ndb
from granary import as1, as2
from httpsig import HeaderVerifier
from httpsig.requests_auth import HTTPSignatureAuth
@ -51,9 +52,19 @@ class ActivityPub(User, Protocol):
"""ActivityPub protocol class.
Key id is AP/AS2 actor id URL. (*Not* fediverse/WebFinger @-@ handle!)
Includes extra `address` computed property for querying entities by handle.
"""
LABEL = 'activitypub'
@ndb.ComputedProperty
def address(self):
return self.ap_address()
def label_id(self):
"""Returns this user's human-readable unique id, eg '@me@snarfed.org'."""
return self.address or self.key.id()
def web_url(self):
"""Returns this user's web URL aka web_url, eg 'https://foo.com/'."""
return util.get_url(self.actor_as2) or self.ap_actor()

Wyświetl plik

@ -174,11 +174,26 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
if self.actor_as2:
return as2.to_as1(self.actor_as2)
def name(self):
"""Returns this user's human-readable name, eg 'Ryan Barrett'."""
if self.actor_as2:
name = self.actor_as2.get('name')
if name:
return name
return self.label_id()
def label_id(self):
"""Returns this user's human-readable unique id, eg '@me@snarfed.org'."""
return self.key.id()
def username(self):
"""Returns the user's preferred username.
Uses stored representative h-card if available, falls back to id.
TODO(#512): move to Web
Returns: str
"""
id = self.key.id()
@ -255,7 +270,7 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
def user_page_path(self, rest=None):
"""Returns the user's Bridgy Fed user page path."""
path = f'/{self.LABEL}/{self.key.id()}'
path = f'/{self.LABEL}/{self.label_id()}'
if rest:
path += f'/{rest}'
return path
@ -263,12 +278,8 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
def user_page_link(self):
"""Returns a pretty user page link with the user's name and profile picture."""
actor = self.actor_as2 or {}
name = (actor.get('name') or
# prettify if domain, noop if username
util.domain_from_link(self.username()))
img = util.get_url(actor, 'icon') or ''
return f'<a class="h-card u-author" href="{self.user_page_path()}"><img src="{img}" class="profile"> {name}</a>'
return f'<a class="h-card u-author" href="{self.user_page_path()}"><img src="{img}" class="profile"> {self.name()}</a>'
class Target(ndb.Model):

Wyświetl plik

@ -1543,17 +1543,20 @@ class ActivityPubUtilsTest(TestCase):
activitypub.postprocess_as2(activitypub.postprocess_as2(obj)),
ignore=['to'])
def test_ap_actor(self):
def test_ap_address(self):
user = ActivityPub(actor_as2={**ACTOR, 'preferredUsername': 'me'})
self.assertEqual('@me@mas.to', user.ap_address())
self.assertEqual('@me@mas.to', user.address)
user = ActivityPub(actor_as2=ACTOR)
self.assertEqual('@swentel@mas.to', user.ap_address())
self.assertEqual('@swentel@mas.to', user.address)
user = ActivityPub(id='https://mas.to/users/alice')
self.assertEqual('@alice@mas.to', user.ap_address())
self.assertEqual('@alice@mas.to', user.address)
def test_ap_address(self):
def test_ap_actor(self):
user = self.make_user('http://foo/actor', cls=ActivityPub)
self.assertEqual('http://foo/actor', user.ap_actor())
@ -1566,3 +1569,10 @@ class ActivityPubUtilsTest(TestCase):
user.actor_as2['url'] = ['http://my/url']
self.assertEqual('http://my/url', user.web_url())
def test_label_id(self):
user = self.make_user('http://foo', cls=ActivityPub)
self.assertEqual('http://foo', user.label_id())
user.actor_as2 = ACTOR
self.assertEqual('@swentel@mas.to', user.label_id())

Wyświetl plik

@ -230,7 +230,7 @@ class FollowTest(testutil.TestCase):
def test_callback_user_use_instead(self, mock_get, mock_post):
user = self.make_user('www.alice.com')
self.user.use_instead = user.key
user.put()
self.user.put()
mock_get.side_effect = (
requests_response(''),

Wyświetl plik

@ -94,6 +94,18 @@ class UserTest(TestCase):
'https://user', '://y.z'):
self.assertFalse(g.user.is_web_url(url), url)
def test_name(self):
self.assertEqual('y.z', g.user.name())
g.user.actor_as2 = {'id': 'abc'}
self.assertEqual('y.z', g.user.name())
g.user.actor_as2 = {'name': 'alice'}
self.assertEqual('alice', g.user.name())
def test_label_id(self):
self.assertEqual('y.z', g.user.label_id())
class ObjectTest(TestCase):
def setUp(self):

Wyświetl plik

@ -1501,6 +1501,9 @@ http://this/404s
'preferredUsername': 'user.com',
})
def test_label_id(self, _, __):
self.assertEqual('user.com', self.user.label_id())
def test_web_url(self, _, __):
self.assertEqual('https://user.com/', self.user.web_url())

4
web.py
Wyświetl plik

@ -49,6 +49,10 @@ class Web(User, Protocol):
def _get_kind(cls):
return 'MagicKey'
def label_id(self):
# prettify if domain, noop if username
return util.domain_from_link(self.username(), minimize=False)
def web_url(self):
"""Returns this user's web URL aka web_url, eg 'https://foo.com/'."""
return f'https://{self.key.id()}/'