Can now serve track from remote library

This commit is contained in:
Eliot Berriot 2018-04-07 15:34:35 +02:00
commit 9612b1bace
No known key found for this signature in database
GPG key ID: DD6965E2476E5C27
22 changed files with 270 additions and 138 deletions

View file

@ -58,21 +58,12 @@ OBJECT_TYPES = [
'Video',
] + ACTIVITY_TYPES
def deliver(activity, on_behalf_of, to=[]):
from . import actors
logger.info('Preparing activity delivery to %s', to)
auth = requests_http_signature.HTTPSignatureAuth(
use_auth_header=False,
headers=[
'(request-target)',
'user-agent',
'host',
'date',
'content-type',],
algorithm='rsa-sha256',
key=on_behalf_of.private_key.encode('utf-8'),
key_id=on_behalf_of.private_key_id,
)
auth = signing.get_auth(
on_behalf_of.private_key, on_behalf_of.private_key_id)
for url in to:
recipient_actor = actors.get_actor(url)
logger.debug('delivering to %s', recipient_actor.inbox_url)

View file

@ -13,8 +13,10 @@ from rest_framework.exceptions import PermissionDenied
from dynamic_preferences.registries import global_preferences_registry
from . import activity
from . import keys
from . import models
from . import serializers
from . import signing
from . import utils
logger = logging.getLogger(__name__)
@ -51,24 +53,37 @@ class SystemActor(object):
additional_attributes = {}
manually_approves_followers = False
def get_request_auth(self):
actor = self.get_actor_instance()
return signing.get_auth(
actor.private_key, actor.private_key_id)
def serialize(self):
actor = self.get_actor_instance()
serializer = serializers.ActorSerializer(actor)
return serializer.data
def get_actor_instance(self):
try:
return models.Actor.objects.get(url=self.get_actor_url())
except models.Actor.DoesNotExist:
pass
private, public = keys.get_key_pair()
args = self.get_instance_argument(
self.id,
name=self.name,
summary=self.summary,
**self.additional_attributes
)
url = args.pop('url')
a, created = models.Actor.objects.get_or_create(
url=url,
defaults=args,
)
return a
args['private_key'] = private.decode('utf-8')
args['public_key'] = public.decode('utf-8')
return models.Actor.objects.create(**args)
def get_actor_url(self):
return utils.full_url(
reverse(
'federation:instance-actors-detail',
kwargs={'actor': self.id}))
def get_instance_argument(self, id, name, summary, **kwargs):
preferences = global_preferences_registry.manager()
@ -78,10 +93,7 @@ class SystemActor(object):
'type': 'Person',
'name': name.format(host=settings.FEDERATION_HOSTNAME),
'manually_approves_followers': True,
'url': utils.full_url(
reverse(
'federation:instance-actors-detail',
kwargs={'actor': id})),
'url': self.get_actor_url(),
'shared_inbox_url': utils.full_url(
reverse(
'federation:instance-actors-inbox',

View file

@ -51,6 +51,8 @@ class SignatureAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
setattr(request, 'actor', None)
actor = self.authenticate_actor(request)
if not actor:
return
user = AnonymousUser()
setattr(request, 'actor', actor)
return (user, None)

View file

@ -1,53 +0,0 @@
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from dynamic_preferences.registries import global_preferences_registry
from funkwhale_api.federation import keys
class Command(BaseCommand):
help = (
'Generate a public/private key pair for your instance,'
' for federation purposes. If a key pair already exists, does nothing.'
)
def add_arguments(self, parser):
parser.add_argument(
'--replace',
action='store_true',
dest='replace',
default=False,
help='Replace existing key pair, if any',
)
parser.add_argument(
'--noinput', '--no-input', action='store_false', dest='interactive',
help="Do NOT prompt the user for input of any kind.",
)
@transaction.atomic
def handle(self, *args, **options):
preferences = global_preferences_registry.manager()
existing_public = preferences['federation__public_key']
existing_private = preferences['federation__public_key']
if existing_public or existing_private and not options['replace']:
raise CommandError(
'Keys are already present! '
'Replace them with --replace if you know what you are doing.')
if options['interactive']:
message = (
'Are you sure you want to do this?\n\n'
"Type 'yes' to continue, or 'no' to cancel: "
)
if input(''.join(message)) != 'yes':
raise CommandError("Operation cancelled.")
private, public = keys.get_key_pair()
preferences['federation__public_key'] = public.decode('utf-8')
preferences['federation__private_key'] = private.decode('utf-8')
self.stdout.write(
'Your new key pair was generated.'
'Your public key is now:\n\n{}'.format(public.decode('utf-8'))
)

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.3 on 2018-04-07 08:52
# Generated by Django 2.0.3 on 2018-04-07 10:10
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
@ -10,7 +10,6 @@ import uuid
class Migration(migrations.Migration):
dependencies = [
('music', '0022_importbatch_import_request'),
('federation', '0002_auto_20180403_1620'),
]
@ -70,7 +69,6 @@ class Migration(migrations.Migration):
('title', models.CharField(max_length=500)),
('metadata', django.contrib.postgres.fields.jsonb.JSONField(default={}, max_length=10000)),
('library', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracks', to='federation.Library')),
('local_track_file', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='library_track', to='music.TrackFile')),
],
),
migrations.AddField(

View file

@ -192,13 +192,6 @@ class LibraryTrack(models.Model):
published_date = models.DateTimeField(null=True, blank=True)
library = models.ForeignKey(
Library, related_name='tracks', on_delete=models.CASCADE)
local_track_file = models.OneToOneField(
'music.TrackFile',
related_name='library_track',
on_delete=models.CASCADE,
null=True,
blank=True,
)
artist_name = models.CharField(max_length=500)
album_title = models.CharField(max_length=500)
title = models.CharField(max_length=500)

View file

@ -53,3 +53,18 @@ def verify_django(django_request, public_key):
request.headers[h] = str(v)
prepared_request = request.prepare()
return verify(request, public_key)
def get_auth(private_key, private_key_id):
return requests_http_signature.HTTPSignatureAuth(
use_auth_header=False,
headers=[
'(request-target)',
'user-agent',
'host',
'date',
'content-type'],
algorithm='rsa-sha256',
key=private_key.encode('utf-8'),
key_id=private_key_id,
)