Skip to content
Snippets Groups Projects
Verified Commit 6dcde77b authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Avoid fetching Actor object on every request authentication

parent e0fce268
No related branches found
No related tags found
No related merge requests found
......@@ -56,6 +56,10 @@ FEDERATION_COLLECTION_PAGE_SIZE = env.int(
FEDERATION_MUSIC_NEEDS_APPROVAL = env.bool(
'FEDERATION_MUSIC_NEEDS_APPROVAL', default=True
)
# how much minutes to wait before refetching actor data
# when authenticating
FEDERATION_ACTOR_FETCH_DELAY = env.int(
'FEDERATION_ACTOR_FETCH_DELAY', default=60 * 12)
ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS')
# APP CONFIGURATION
......
import datetime
import logging
import uuid
import xml
......@@ -49,11 +50,20 @@ def get_actor_data(actor_url):
def get_actor(actor_url):
try:
actor = models.Actor.objects.get(url=actor_url)
except models.Actor.DoesNotExist:
actor = None
fetch_delta = datetime.timedelta(
minutes=settings.FEDERATION_ACTOR_FETCH_DELAY)
if actor and actor.last_fetch_date > timezone.now() - fetch_delta:
# cache is hot, we can return as is
return actor
data = get_actor_data(actor_url)
serializer = serializers.ActorSerializer(data=data)
serializer.is_valid(raise_exception=True)
return serializer.build()
return serializer.save(last_fetch_date=timezone.now())
class SystemActor(object):
......
......@@ -25,28 +25,19 @@ class SignatureAuthentication(authentication.BaseAuthentication):
raise exceptions.AuthenticationFailed(str(e))
try:
actor_data = actors.get_actor_data(key_id)
actor = actors.get_actor(key_id.split('#')[0])
except Exception as e:
raise exceptions.AuthenticationFailed(str(e))
try:
public_key = actor_data['publicKey']['publicKeyPem']
except KeyError:
if not actor.public_key:
raise exceptions.AuthenticationFailed('No public key found')
serializer = serializers.ActorSerializer(data=actor_data)
if not serializer.is_valid():
raise exceptions.AuthenticationFailed('Invalid actor payload: {}'.format(serializer.errors))
try:
signing.verify_django(request, public_key.encode('utf-8'))
signing.verify_django(request, actor.public_key.encode('utf-8'))
except cryptography.exceptions.InvalidSignature:
raise exceptions.AuthenticationFailed('Invalid signature')
try:
return models.Actor.objects.get(url=actor_data['id'])
except models.Actor.DoesNotExist:
return serializer.save()
return actor
def authenticate(self, request):
setattr(request, 'actor', None)
......
......@@ -103,9 +103,10 @@ class ActorSerializer(serializers.Serializer):
def save(self, **kwargs):
d = self.prepare_missing_fields()
d.update(kwargs)
return models.Actor.objects.create(
**d
)
return models.Actor.objects.update_or_create(
url=d['url'],
defaults=d,
)[0]
def validate_summary(self, value):
if value:
......
......@@ -29,6 +29,42 @@ def test_actor_fetching(r_mock):
assert r == payload
def test_get_actor(factories, r_mock):
actor = factories['federation.Actor'].build()
payload = serializers.ActorSerializer(actor).data
r_mock.get(actor.url, json=payload)
new_actor = actors.get_actor(actor.url)
assert new_actor.pk is not None
assert serializers.ActorSerializer(new_actor).data == payload
def test_get_actor_use_existing(factories, settings, mocker):
settings.FEDERATION_ACTOR_FETCH_DELAY = 60
actor = factories['federation.Actor']()
get_data = mocker.patch('funkwhale_api.federation.actors.get_actor_data')
new_actor = actors.get_actor(actor.url)
assert new_actor == actor
get_data.assert_not_called()
def test_get_actor_refresh(factories, settings, mocker):
settings.FEDERATION_ACTOR_FETCH_DELAY = 0
actor = factories['federation.Actor']()
payload = serializers.ActorSerializer(actor).data
# actor changed their username in the meantime
payload['preferredUsername'] = 'New me'
get_data = mocker.patch(
'funkwhale_api.federation.actors.get_actor_data',
return_value=payload)
new_actor = actors.get_actor(actor.url)
assert new_actor == actor
assert new_actor.last_fetch_date > actor.last_fetch_date
assert new_actor.preferred_username == 'New me'
def test_get_library(db, settings, mocker):
get_key_pair = mocker.patch(
'funkwhale_api.federation.keys.get_key_pair',
......
Avoid fetching Actor object on every request authentication
......@@ -3,10 +3,12 @@
<div class="content">
<div class="header ellipsis">
<router-link
v-if="library"
:title="displayName"
:to="{name: 'federation.libraries.detail', params: {id: library.uuid }}">
{{ displayName }}
</router-link>
<span :title="displayName" v-else>{{ displayName }}</span>
</div>
</div>
<div class="content">
......
......@@ -150,6 +150,7 @@ export default {
methods: {
fetchData () {
var self = this
this.scanTrigerred = false
this.isLoading = true
let url = 'federation/libraries/' + this.id + '/'
logger.default.debug('Fetching library "' + this.id + '"')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment