diff --git a/fedireads/incoming.py b/fedireads/incoming.py index 5e5a457e..6ca51d0e 100644 --- a/fedireads/incoming.py +++ b/fedireads/incoming.py @@ -90,6 +90,7 @@ def shared_inbox(request): def get_public_key(key_actor): + ''' try a stored key or load it from remote ''' try: user = models.User.objects.get(remote_id=key_actor) public_key = user.public_key diff --git a/fedireads/remote_user.py b/fedireads/remote_user.py index 7708dfaa..1a3a65e1 100644 --- a/fedireads/remote_user.py +++ b/fedireads/remote_user.py @@ -4,6 +4,7 @@ from uuid import uuid4 import requests from django.core.files.base import ContentFile +from django.db import transaction from fedireads import models from fedireads.status import create_review_from_activity @@ -12,7 +13,7 @@ from fedireads.status import create_review_from_activity def get_or_create_remote_user(actor): ''' look up a remote user or add them ''' try: - return models.User.objects.get(actor=actor) + return models.User.objects.get(remote_id=actor) except models.User.DoesNotExist: pass @@ -25,37 +26,50 @@ def get_or_create_remote_user(actor): response.raise_for_status() data = response.json() - # the webfinger format for the username. + # make sure our actor is who they say they are + assert actor == data['id'] + actor_parts = urlparse(actor) + with transaction.atomic(): + user = create_remote_user(data) + user.federated_server = get_or_create_remote_server(actor_parts.netloc) + user.save() + + avatar = get_avatar(data) + user.avatar.save(*avatar) + + if user.fedireads_user: + get_remote_reviews(user) + return user + + +def create_remote_user(data): + ''' parse the activitypub actor data into a user ''' + actor = data['id'] + actor_parts = urlparse(actor) + + # the webfinger format for the username. username = '%s@%s' % (actor_parts.path.split('/')[-1], actor_parts.netloc) + shared_inbox = data.get('endpoints').get('sharedInbox') if \ data.get('endpoints') else None - server = get_or_create_remote_server(actor_parts.netloc) - avatar = get_avatar(data) - # throws a key error if it can't find any of these fields - user = models.User.objects.create_user( + return models.User.objects.create_user( username, '', '', # email and passwords are left blank - actor=actor, + remote_id=actor, name=data.get('name'), summary=data.get('summary'), inbox=data['inbox'], #fail if there's no inbox outbox=data['outbox'], # fail if there's no outbox shared_inbox=shared_inbox, - # TODO: I'm never actually using this for remote users public_key=data.get('publicKey').get('publicKeyPem'), local=False, fedireads_user=data.get('fedireadsUser', False), manually_approves_followers=data.get( 'manuallyApprovesFollowers', False), - federated_server=server, ) - user.avatar.save(*avatar) - if user.fedireads_user: - get_remote_reviews(user) - return user def get_avatar(data): diff --git a/fedireads/tests/data/ap_user.json b/fedireads/tests/data/ap_user.json new file mode 100644 index 00000000..16b7fe21 --- /dev/null +++ b/fedireads/tests/data/ap_user.json @@ -0,0 +1,36 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value" + } + ], + "id": "https://example.com/user/mouse", + "type": "Person", + "preferredUsername": "mouse", + "name": "MOUSE?? MOUSE!!", + "inbox": "https://example.com/user/mouse/inbox", + "outbox": "https://example.com/user/mouse/outbox", + "followers": "https://example.com/user/mouse/followers", + "following": "https://example.com/user/mouse/following", + "summary": "", + "publicKey": { + "id": "https://example.com/user/mouse/#main-key", + "owner": "https://example.com/user/mouse", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6QisDrjOQvkRo/MqNmSYPwqtt\nCxg/8rCW+9jKbFUKvqjTeKVotEE85122v/DCvobCCdfQuYIFdVMk+dB1xJ0iPGPg\nyU79QHY22NdV9mFKA2qtXVVxb5cxpA4PlwOHM6PM/k8B+H09OUrop2aPUAYwy+vg\n+MXyz8bAXrIS1kq6fQIDAQAB\n-----END PUBLIC KEY-----" + }, + "endpoints": { + "sharedInbox": "https://example.com/inbox" + }, + "fedireadsUser": true, + "manuallyApprovesFollowers": false, + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://example.com/images/avatars/AL-2-crop-50.png" + } +} diff --git a/fedireads/tests/test_remote_user.py b/fedireads/tests/test_remote_user.py new file mode 100644 index 00000000..374c2e2c --- /dev/null +++ b/fedireads/tests/test_remote_user.py @@ -0,0 +1,37 @@ +from django.test import TestCase +import json +import pathlib + +from fedireads import models, remote_user + + +class RemoteUser(TestCase): + ''' not too much going on in the books model but here we are ''' + def setUp(self): + self.remote_user = models.User.objects.create_user( + 'mouse', 'mouse@mouse.com', 'mouseword', + local=False, + remote_id='https://example.com/users/mouse', + inbox='https://example.com/users/mouse/inbox', + outbox='https://example.com/users/mouse/outbox', + ) + + def test_get_remote_user(self): + actor = 'https://example.com/users/mouse' + user = remote_user.get_or_create_remote_user(actor) + self.assertEqual(user, self.remote_user) + + + def test_create_remote_user(self): + datafile = pathlib.Path(__file__).parent.joinpath('data/ap_user.json') + data = json.loads(datafile.read_bytes()) + user = remote_user.create_remote_user(data) + self.assertEqual(user.username, 'mouse@example.com') + self.assertEqual(user.name, 'MOUSE?? MOUSE!!') + self.assertEqual(user.inbox, 'https://example.com/user/mouse/inbox') + self.assertEqual(user.outbox, 'https://example.com/user/mouse/outbox') + self.assertEqual(user.shared_inbox, 'https://example.com/inbox') + self.assertEqual(user.public_key, data['publicKey']['publicKeyPem']) + self.assertEqual(user.local, False) + self.assertEqual(user.fedireads_user, True) + self.assertEqual(user.manually_approves_followers, False)