Adds username/localname fields

This commit is contained in:
Mouse Reeve 2020-01-27 20:56:45 -08:00
parent 8dfad68ca7
commit b2b651a22f
5 changed files with 34 additions and 14 deletions

View file

@ -41,7 +41,7 @@ def get_actor(request, username):
if request.method != 'GET': if request.method != 'GET':
return HttpResponseBadRequest() return HttpResponseBadRequest()
user = models.User.objects.get(username=username) user = models.User.objects.get(localname=username)
return JsonResponse({ return JsonResponse({
'@context': [ '@context': [
'https://www.w3.org/ns/activitystreams', 'https://www.w3.org/ns/activitystreams',
@ -50,7 +50,7 @@ def get_actor(request, username):
'id': user.actor, 'id': user.actor,
'type': 'Person', 'type': 'Person',
'preferredUsername': user.username, 'preferredUsername': user.localname,
'inbox': format_inbox(user), 'inbox': format_inbox(user),
'followers': '%s/followers' % user.actor, 'followers': '%s/followers' % user.actor,
'publicKey': { 'publicKey': {
@ -95,9 +95,11 @@ def handle_account_search(query):
try: try:
user = models.User.objects.get(username=query) user = models.User.objects.get(username=query)
except models.User.DoesNotExist: except models.User.DoesNotExist:
url = 'https://%s/.well-known/webfinger' % domain url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \
params = {'resource': 'acct:%s' % query} (domain, query)
response = requests.get(url, params=params) response = requests.get(url)
if not response.ok:
response.raise_for_status()
data = response.json() data = response.json()
for link in data['links']: for link in data['links']:
if link['rel'] == 'self': if link['rel'] == 'self':
@ -148,7 +150,7 @@ def handle_incoming_follow(activity):
) )
to_follow = models.User.objects.get(username=to_follow) to_follow = models.User.objects.get(username=to_follow)
# figure out who they are # 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) to_follow.followers.add(user)
# verify uuid and accept the request # verify uuid and accept the request
models.FollowActivity( models.FollowActivity(
@ -184,6 +186,7 @@ def handle_outgoing_follow(user, to_follow):
models.FollowActivity( models.FollowActivity(
uuid=uuid, uuid=uuid,
user=user, user=user,
followed=to_follow,
content=activity, content=activity,
).save() ).save()
@ -279,7 +282,7 @@ def handle_review(user, book, name, content, rating):
@csrf_exempt @csrf_exempt
def outbox(request, username): def outbox(request, username):
''' outbox for the requested user ''' ''' 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() size = models.Review.objects.filter(user=user).count()
if request.method == 'GET': if request.method == 'GET':
# list of activities # list of activities
@ -305,7 +308,7 @@ def broadcast(sender, action, recipients):
def sign_and_send(sender, action, destination): def sign_and_send(sender, action, destination):
''' crpyto whatever and http junk ''' ''' 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() now = datetime.utcnow().isoformat()
message_to_sign = '''(request-target): post %s message_to_sign = '''(request-target): post %s
host: https://%s host: https://%s
@ -313,7 +316,7 @@ date: %s''' % (inbox_fragment, DOMAIN, now)
signer = pkcs1_15.new(RSA.import_key(sender.private_key)) signer = pkcs1_15.new(RSA.import_key(sender.private_key))
signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8'))) 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 += 'headers="(request-target) host date",'
signature += 'signature="%s"' % b64encode(signed_message) signature += 'signature="%s"' % b64encode(signed_message)
response = requests.post( response = requests.post(

View file

@ -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 from django.conf import settings
import django.contrib.auth.models import django.contrib.auth.models
@ -37,6 +37,7 @@ class Migration(migrations.Migration):
('api_key', models.CharField(blank=True, max_length=255, null=True)), ('api_key', models.CharField(blank=True, max_length=255, null=True)),
('actor', models.CharField(max_length=255)), ('actor', models.CharField(max_length=255)),
('local', models.BooleanField(default=True)), ('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)), ('created_date', models.DateTimeField(auto_now_add=True)),
('updated_date', models.DateTimeField(auto_now=True)), ('updated_date', models.DateTimeField(auto_now=True)),
('followers', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), ('followers', models.ManyToManyField(to=settings.AUTH_USER_MODEL)),

View file

@ -15,6 +15,12 @@ class User(AbstractUser):
api_key = models.CharField(max_length=255, blank=True, null=True) api_key = models.CharField(max_length=255, blank=True, null=True)
actor = models.CharField(max_length=255) actor = models.CharField(max_length=255)
local = models.BooleanField(default=True) 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 # TODO: a field for if non-local users are readers or others
followers = models.ManyToManyField('self', symmetrical=False) followers = models.ManyToManyField('self', symmetrical=False)
created_date = models.DateTimeField(auto_now_add=True) created_date = models.DateTimeField(auto_now_add=True)
@ -28,11 +34,16 @@ class User(AbstractUser):
self.private_key = key.export_key().decode('utf8') self.private_key = key.export_key().decode('utf8')
self.public_key = key.publickey().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): 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) 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) super().save(*args, **kwargs)
@ -137,7 +148,7 @@ class Shelf(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.identifier: if not self.identifier:
self.identifier = '%s_%s' % ( self.identifier = '%s_%s' % (
self.user.username, self.user.localname,
re.sub(r'\W', '-', self.name).lower() re.sub(r'\W', '-', self.name).lower()
) )
if not self.activitypub_id: if not self.activitypub_id:

View file

@ -27,7 +27,7 @@ DEBUG = True
DOMAIN = 'bd352ee8.ngrok.io' DOMAIN = 'bd352ee8.ngrok.io'
ALLOWED_HOSTS = ['localhost', DOMAIN] ALLOWED_HOSTS = ['*']
OL_URL = 'https://openlibrary.org' OL_URL = 'https://openlibrary.org'
# Application definition # Application definition

View file

@ -7,6 +7,7 @@ from django.template.response import TemplateResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from fedireads import models, openlibrary from fedireads import models, openlibrary
from fedireads import federation as api from fedireads import federation as api
from fedireads.settings import DOMAIN
import re import re
@login_required @login_required
@ -147,3 +148,7 @@ def search(request):
return TemplateResponse(request, 'results.html', {'results': results}) 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, '')