diff --git a/fedireads/federation.py b/fedireads/federation.py index 8480c2b0a..8e0dbd1b9 100644 --- a/fedireads/federation.py +++ b/fedireads/federation.py @@ -41,7 +41,7 @@ def get_actor(request, username): if request.method != 'GET': return HttpResponseBadRequest() - user = models.User.objects.get(username=username) + user = models.User.objects.get(localname=username) return JsonResponse({ '@context': [ 'https://www.w3.org/ns/activitystreams', @@ -50,7 +50,7 @@ def get_actor(request, username): 'id': user.actor, 'type': 'Person', - 'preferredUsername': user.username, + 'preferredUsername': user.localname, 'inbox': format_inbox(user), 'followers': '%s/followers' % user.actor, 'publicKey': { @@ -95,9 +95,11 @@ def handle_account_search(query): try: user = models.User.objects.get(username=query) except models.User.DoesNotExist: - url = 'https://%s/.well-known/webfinger' % domain - params = {'resource': 'acct:%s' % query} - response = requests.get(url, params=params) + url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \ + (domain, query) + response = requests.get(url) + if not response.ok: + response.raise_for_status() data = response.json() for link in data['links']: if link['rel'] == 'self': @@ -148,7 +150,7 @@ def handle_incoming_follow(activity): ) to_follow = models.User.objects.get(username=to_follow) # figure out who they are - user = get_or_create_remote_user(activity) + user = get_or_create_remote_user(activity['actor']) to_follow.followers.add(user) # verify uuid and accept the request models.FollowActivity( @@ -184,6 +186,7 @@ def handle_outgoing_follow(user, to_follow): models.FollowActivity( uuid=uuid, user=user, + followed=to_follow, content=activity, ).save() @@ -279,7 +282,7 @@ def handle_review(user, book, name, content, rating): @csrf_exempt def outbox(request, username): ''' outbox for the requested user ''' - user = models.User.objects.get(username=username) + user = models.User.objects.get(localname=username) size = models.Review.objects.filter(user=user).count() if request.method == 'GET': # list of activities @@ -305,7 +308,7 @@ def broadcast(sender, action, recipients): def sign_and_send(sender, action, destination): ''' crpyto whatever and http junk ''' - inbox_fragment = '/api/u/%s/inbox' % (sender.username) + inbox_fragment = '/api/u/%s/inbox' % (sender.localname) now = datetime.utcnow().isoformat() message_to_sign = '''(request-target): post %s host: https://%s @@ -313,7 +316,7 @@ date: %s''' % (inbox_fragment, DOMAIN, now) signer = pkcs1_15.new(RSA.import_key(sender.private_key)) signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8'))) - signature = 'keyId="%s",' % sender.username + signature = 'keyId="%s",' % sender.localname signature += 'headers="(request-target) host date",' signature += 'signature="%s"' % b64encode(signed_message) response = requests.post( diff --git a/fedireads/migrations/0001_initial.py b/fedireads/migrations/0001_initial.py index c595e3d5e..a3d64c684 100644 --- a/fedireads/migrations/0001_initial.py +++ b/fedireads/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.2 on 2020-01-28 03:40 +# Generated by Django 3.0.2 on 2020-01-28 04:47 from django.conf import settings import django.contrib.auth.models @@ -37,6 +37,7 @@ class Migration(migrations.Migration): ('api_key', models.CharField(blank=True, max_length=255, null=True)), ('actor', models.CharField(max_length=255)), ('local', models.BooleanField(default=True)), + ('localname', models.CharField(blank=True, max_length=255, null=True, unique=True)), ('created_date', models.DateTimeField(auto_now_add=True)), ('updated_date', models.DateTimeField(auto_now=True)), ('followers', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), diff --git a/fedireads/models.py b/fedireads/models.py index 02492ba5c..6808e79d5 100644 --- a/fedireads/models.py +++ b/fedireads/models.py @@ -15,6 +15,12 @@ class User(AbstractUser): api_key = models.CharField(max_length=255, blank=True, null=True) actor = models.CharField(max_length=255) local = models.BooleanField(default=True) + localname = models.CharField( + max_length=255, + null=True, + blank=True, + unique=True + ) # TODO: a field for if non-local users are readers or others followers = models.ManyToManyField('self', symmetrical=False) created_date = models.DateTimeField(auto_now_add=True) @@ -28,11 +34,16 @@ class User(AbstractUser): self.private_key = key.export_key().decode('utf8') self.public_key = key.publickey().export_key().decode('utf8') - if self.local and not self.actor: - self.actor = 'https://%s/api/u/%s' % (DOMAIN, self.username) if self.local and not re.match(r'\w+@\w+.\w+', self.username): + # set your local username that doesn't have the domain self.username = '%s@%s' % (self.username, DOMAIN) + if self.local and not self.localname: + self.localname = self.username.replace('@%s' % DOMAIN, '') + + if self.local and not self.actor: + self.actor = 'https://%s/api/u/%s' % (DOMAIN, self.localname) + super().save(*args, **kwargs) @@ -137,7 +148,7 @@ class Shelf(models.Model): def save(self, *args, **kwargs): if not self.identifier: self.identifier = '%s_%s' % ( - self.user.username, + self.user.localname, re.sub(r'\W', '-', self.name).lower() ) if not self.activitypub_id: diff --git a/fedireads/settings.py b/fedireads/settings.py index 568f057a9..598c4dbdb 100644 --- a/fedireads/settings.py +++ b/fedireads/settings.py @@ -27,7 +27,7 @@ DEBUG = True DOMAIN = 'bd352ee8.ngrok.io' -ALLOWED_HOSTS = ['localhost', DOMAIN] +ALLOWED_HOSTS = ['*'] OL_URL = 'https://openlibrary.org' # Application definition diff --git a/fedireads/views.py b/fedireads/views.py index 963514bc5..6e36bdb06 100644 --- a/fedireads/views.py +++ b/fedireads/views.py @@ -7,6 +7,7 @@ from django.template.response import TemplateResponse from django.views.decorators.csrf import csrf_exempt from fedireads import models, openlibrary from fedireads import federation as api +from fedireads.settings import DOMAIN import re @login_required @@ -147,3 +148,7 @@ def search(request): return TemplateResponse(request, 'results.html', {'results': results}) + +def simplify_local_username(user): + ''' helper for getting the short username for local users ''' + return user.username.replace('@%s' % DOMAIN, '')