From 37d13648bdf62f3059faf9790b4362f52bc29b28 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy <c.mackervoy@gmail.com> Date: Tue, 7 Jul 2020 08:35:55 +0000 Subject: [PATCH] bugfix: Model.get_or_create_external --- djangoldp/activities/services.py | 2 +- djangoldp/models.py | 20 ++++++++++++++++++-- djangoldp/serializers.py | 2 +- djangoldp/tests/tests_inbox.py | 24 ++++++++++++++++++++++++ djangoldp/views.py | 7 +++++-- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/djangoldp/activities/services.py b/djangoldp/activities/services.py index 4b817d9f..c478eac3 100644 --- a/djangoldp/activities/services.py +++ b/djangoldp/activities/services.py @@ -210,7 +210,6 @@ class ActivityPubService(object): for field_name, relation_info in info.relations.items(): if not relation_info.to_many: value = getattr(instance, field_name, None) - logger.debug('[Sender] model has relation ' + str(value)) if value is not None and Model.is_external(value): target_type = Model.get_model_rdf_type(type(value)) @@ -218,6 +217,7 @@ class ActivityPubService(object): continue targets.add(value.urlid) + logger.debug('[Sender] model has external relation ' + str(value.urlid)) return targets diff --git a/djangoldp/models.py b/djangoldp/models.py index d970ac35..65638a41 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -192,6 +192,16 @@ class Model(models.Model): field_tuples['username'] = str(uuid.uuid4()) return model.objects.create(urlid=urlid, is_backlink=True, **field_tuples) + @classonlymethod + def get_or_create_external(cls, model, urlid, **kwargs): + ''' + checks that the parameterised urlid is external and then returns the result of Model.get_or_create + :raises ObjectDoesNotExist: if the urlid is not external and the object doesn't exist + ''' + if not Model.is_external(urlid) and not model.objects.filter(urlid=urlid).exists(): + raise ObjectDoesNotExist + return Model.get_or_create(model, urlid, **kwargs) + @classonlymethod def get_model_rdf_type(cls, model): if model is get_user_model(): @@ -234,9 +244,15 @@ class Model(models.Model): @classmethod def is_external(cls, value): - '''returns True if the urlid of the value passed is from an external source''' + ''' + :param value: string urlid or an instance with urlid field + :return: True if the urlid is external to the server, False otherwise + ''' try: - return value.urlid is not None and not value.urlid.startswith(settings.SITE_URL) + if not isinstance(value, str): + value = value.urlid + + return value is not None and not value.startswith(settings.SITE_URL) except: return False diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 62543396..b7da7239 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -627,7 +627,7 @@ class LDPSerializer(HyperlinkedModelSerializer): model, sub_inst = Model.resolve(field_dict['urlid']) # remote resource - get backlinked copy elif hasattr(field_model, 'urlid'): - sub_inst = Model.get_or_create(field_model, field_dict['urlid']) + sub_inst = Model.get_or_create_external(field_model, field_dict['urlid']) # try slug field, assuming that this is a local resource elif slug_field in field_dict: kwargs = {slug_field: field_dict[slug_field]} diff --git a/djangoldp/tests/tests_inbox.py b/djangoldp/tests/tests_inbox.py index b11f7ddb..834228e5 100644 --- a/djangoldp/tests/tests_inbox.py +++ b/djangoldp/tests/tests_inbox.py @@ -1,5 +1,6 @@ import json from django.contrib.auth import get_user_model +from django.conf import settings from django.db import IntegrityError from rest_framework.test import APIClient, APITestCase from djangoldp.tests.models import Circle, CircleMember, Project, UserProfile, DateModel, DateChild @@ -75,6 +76,29 @@ class TestsInbox(APITestCase): self.assertEqual(circles[0].owner, self.user) self._assert_activity_created(response) + # sender has sent a circle with a local user that doesn't exist + def test_create_activity_circle_local(self): + urlid = '{}{}'.format(settings.SITE_URL, 'someonewhodoesntexist') + obj = { + "@type": "hd:circle", + "@id": "https://distant.com/circles/1/", + "owner": { + "@type": "foaf:user", + "@id": urlid + } + } + payload = self._get_activity_request_template("Create", obj) + + prior_users_length = get_user_model().objects.count() + + response = self.client.post('/inbox/', + data=json.dumps(payload), content_type='application/ld+json') + self.assertEqual(response.status_code, 404) + + # assert that the circle was not created neither a backlinked user + self.assertEquals(Circle.objects.count(), 0) + self.assertEquals(get_user_model().objects.count(), prior_users_length) + # # ADD ACTIVITIES # diff --git a/djangoldp/views.py b/djangoldp/views.py index 5f056fd5..6a2dfb8b 100644 --- a/djangoldp/views.py +++ b/djangoldp/views.py @@ -4,7 +4,7 @@ from django.apps import apps from django.conf import settings from django.conf.urls import url, include from django.contrib.auth import get_user_model -from django.core.exceptions import FieldDoesNotExist +from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist from django.core.urlresolvers import get_resolver from django.db import IntegrityError, transaction from django.http import JsonResponse, Http404 @@ -157,7 +157,10 @@ class InboxView(APIView): branches[item[0]] = backlink # get or create the backlink - return Model.get_or_create(object_model, obj['@id'], update=update, **branches) + try: + return Model.get_or_create_external(object_model, obj['@id'], update=update, **branches) + except ObjectDoesNotExist: + raise Http404() # TODO: a fallback here? Saving the backlink as Object or similar def _get_subclass_with_rdf_type_or_404(self, rdf_type): -- GitLab