Merge branch 'actor-cache' into 'develop'
Avoid fetching Actor object on every request authentication See merge request funkwhale/funkwhale!171
This commit is contained in:
commit
797e0bd2ca
|
@ -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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
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',
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
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 + '"')
|
||||
|
|
Loading…
Reference in New Issue