Пример #1
0
    def test_atstyle(self):

        httpretty.register_uri(
            'GET',
            EXAMPLE_USER_URL,
            status=200,
            headers={
                'Content-Type': 'application/activity+json',
            },
            body=EXAMPLE_USER_RESULT,
        )

        httpretty.register_uri(
            'GET',
            EXAMPLE_WEBFINGER_URL,
            status=200,
            headers={
                'Content-Type': 'application/jrd+json',
            },
            body=EXAMPLE_WEBFINGER_RESULT,
        )

        user = fetch(EXAMPLE_ATSTYLE, RemotePerson)

        self._asserts_for_example_user(user)
Пример #2
0
    def test_fetch_known_user(self):

        existing = RemotePerson(remote_url=EXAMPLE_USER_URL, )
        existing.save()

        found = {}

        def finding(request, uri, headers):
            found['found'] = True
            return EXAMPLE_USER_RESULT

        httpretty.register_uri(
            'GET',
            EXAMPLE_USER_URL,
            status=200,
            headers={
                'Content-Type': 'application/activity+json',
            },
            body=finding,
        )

        user = fetch(EXAMPLE_USER_URL, RemotePerson)

        self.assertNotIn(
            'found',
            found,
            msg="Known remote user wasn't re-fetched",
        )
Пример #3
0
    def test_url_404(self):
        found = fetch('https://testserver/users/bob', expected_type=Person)

        self.assertEqual(
            found,
            None,
        )
Пример #4
0
    def __next__(self):
        if self._iter_items:
            return self._iter_items.pop(0)

        if self._next_page is None:
            logger.debug("%s: iteration: finished!", self.url)
            raise StopIteration

        logger.debug("%s: iteration: fetching %s...", self.url,
                     self._next_page)

        import kepi.sombrero_sendpub.fetch as fetch

        next_bit = fetch.fetch(
            self._next_page,
            expected_type=_CollectionPage,
        )

        if next_bit is None:
            logger.info("%s: error in fetching items", self.url)
            raise StopIteration

        self._iter_items = next_bit.items
        self._next_page = next_bit.next

        logger.debug('  -- containing %s', self._iter_items)

        return self._iter_items.pop(0)
Пример #5
0
    def test_when_sender_is_followed_by_local_users(self):

        from kepi.trilby_api.models import Follow, Person

        local_user = create_local_person()
        remote_alice = fetch(REMOTE_ALICE, expected_type=Person)

        following = Follow(
            follower=local_user,
            following=remote_alice,
        )
        following.save()

        object_form = {
            'id': 'https://example.com/some-note',
            'type': 'Note',
            'content': 'Lorem ipsum',
        }

        status = self._send_create_for_object(object_form, sender=remote_alice)

        self.assertIsNotNone(
            status,
            msg='it creates status',
        )

        self.assertEqual(
            status.text,
            'Lorem ipsum',
            msg='it creates status text',
        )
Пример #6
0
    def test_fetch_simple_collection(self):
        httpretty.register_uri(
            'GET',
            EXAMPLE_SIMPLE_COLLECTION_URL,
            status=200,
            headers={
                'Content-Type': 'application/activity+json',
            },
            body=EXAMPLE_SIMPLE_COLLECTION,
        )

        collection = fetch(EXAMPLE_SIMPLE_COLLECTION_URL,
                           expected_type=Collection)

        self.assertEqual(sorted(collection),
                         EXAMPLE_SIMPLE_COLLECTION_MEMBERS,
                         msg="Collection can be iterated")

        self.assertEqual(sorted(collection),
                         EXAMPLE_SIMPLE_COLLECTION_MEMBERS,
                         msg="Collection can be iterated twice")

        self.assertEqual(len(collection),
                         len(EXAMPLE_SIMPLE_COLLECTION_MEMBERS),
                         msg="Collection has a length")
Пример #7
0
    def test_url(self):

        found = fetch('https://testserver/users/alice', expected_type=Person)

        self.assertEqual(
            found,
            self._alice,
        )
Пример #8
0
    def test_atstyle_404(self):

        found = fetch('bob@testserver', expected_type=Person)

        self.assertEqual(
            found,
            None,
        )
Пример #9
0
    def test_url_wrong_type(self):
        found = fetch('https://testserver/users/bob/outbox',
                      expected_type=Person)

        self.assertEqual(
            found,
            None,
        )
Пример #10
0
    def test_atstyle(self):

        found = fetch('alice@testserver', expected_type=Person)

        self.assertEqual(
            found,
            self._alice,
        )
Пример #11
0
def on_announce(fields, address):

    logger.debug('%s: on_announce %s', address, fields)

    try:
        if isinstance(fields.get('object', None), dict):
            # We don't trust an object passed to us as part of
            # an Announce, because it generally comes from a
            # different user. So we take the id and go and
            # look it up for ourselves.
            status_url = fields['object']['id']
        else:
            status_url = fields['object']
    except FieldError as fe:
        logger.info("%s: unusable object field: %s",
                address, fe)
        return None

    status = sombrero_fetch.fetch(status_url,
            expected_type = trilby_models.Status,
            )

    if status is None:

        logger.info("%s: attempted to reblog non-existent status %s",
                address, status_url)
        return None

    actor = sombrero_fetch.fetch(fields['actor'],
            expected_type = trilby_models.Person,
            )

    logger.debug('%s: reblogging status %s by %s',
            address, status_url, actor)

    reblog = trilby_models.Status(
            account = actor,
            reblog_of = status,
            )
    reblog.save()

    logger.debug('%s: created reblog: %s',
            address, reblog)

    return reblog
Пример #12
0
def on_follow(fields, address):

    logger.debug('%s: on_follow %s', address, fields)

    if not bowler_utils.is_local(fields['object']):
        logger.info("%s: ignoring someone following non-local user",
                address)
        return None

    follower = sombrero_fetch.fetch(
            fields['actor'],
            expected_type = trilby_models.Person,
            )

    if follower is None:
        # shouldn't happen
        logger.warning('%s: could not find remote user %s',
                address,
                fields['actor'],
                )
        return None

    following = sombrero_fetch.fetch(
            fields['object'],
            expected_type = trilby_models.Person,
            )

    if following is None:
        logger.info('%s: there is no local user %s',
                address,
                fields['object'],
                )
        return None

    result = trilby_models.Follow(
            follower = follower,
            following = following,
            offer = fields.get('id'),
            )

    result.save(
            send_signal = True,
            )

    return result
Пример #13
0
    def test_atstyle_410(self):

        httpretty.register_uri(
            'GET',
            EXAMPLE_WEBFINGER_URL,
            status=410,
            headers={
                'Content-Type': 'text/plain',
            },
            body="not any more!",
        )

        fetch(EXAMPLE_ATSTYLE, RemotePerson)

        users = RemotePerson.objects.filter(acct=EXAMPLE_ATSTYLE)

        self.assertEqual(
            len(users),
            0,
        )
Пример #14
0
    def test_atstyle_no_activity(self):

        httpretty.register_uri(
            'GET',
            EXAMPLE_WEBFINGER_URL,
            status=200,
            headers={
                'Content-Type': 'application/jrd+json',
            },
            body=EXAMPLE_WEBFINGER_RESULT_NO_USER,
        )

        fetch(EXAMPLE_ATSTYLE, RemotePerson)

        users = RemotePerson.objects.filter(acct=EXAMPLE_ATSTYLE)

        self.assertEqual(
            len(users),
            0,
        )
Пример #15
0
def on_like(fields, address):

    logger.debug('%s: on_like %s', address, fields)

    liker = sombrero_fetch.fetch(
            fields['actor'],
            expected_type = trilby_models.Person,
            )

    if liker is None:
        # shouldn't happen
        logger.warning('%s: could not find user %s',
                address,
                fields['actor'],
                )
        return None

    liked = sombrero_fetch.fetch(
            fields['object'],
            expected_type = trilby_models.Status,
            )

    if liked is None:
        logger.info('%s: could not find status %s',
                address,
                fields['object'],
                )
        return None

    like = trilby_models.Like(
            liker = liker,
            liked = liked,
            )

    like.save(
            send_signal = True,
            )

    return like
Пример #16
0
    def test_fetch_410(self):
        httpretty.register_uri(
            'GET',
            EXAMPLE_USER_URL,
            status=410,
            headers={
                'Content-Type': 'text/plain',
            },
            body='not any more!',
        )

        user = fetch(EXAMPLE_USER_URL, RemotePerson)

        self.assertIsNone(user, )
Пример #17
0
    def test_fetch_no_such_host(self):
        def no_such_host(request, uri, headers):
            raise requests.ConnectionError()

        httpretty.register_uri(
            'GET',
            EXAMPLE_USER_URL,
            status=200,
            headers={
                'Content-Type': 'text/plain',
            },
            body=no_such_host,
        )

        with suppress_thread_exceptions():
            user = fetch(EXAMPLE_USER_URL, RemotePerson)

        self.assertIsNone(user, )
Пример #18
0
            def __next__(self):

                logger.debug("%s RemotePerson: finding next...",
                        self.address,
                        )

                url = self.collection.__next__()

                logger.debug("%s RemotePerson: next is at %s",
                        self.address,
                        url,
                        )

                person = fetch(
                        url,
                        Person,
                        )

                logger.debug("%s RemotePerson:  -- which is %s",
                        url,
                        person,
                        )

                return person
Пример #19
0
            def __iter__(self):

                remote_collection = fetch(
                        self.address,
                        Collection,
                        )

                if remote_collection is None:
                    logger.debug(
                            "%s RemotePerson: could not retrieve collection",
                            self.address,
                            )
                    self.collection = [].__iter__()
                    return self

                self.collection = remote_collection.__iter__()

                logger.debug(
                        "%s: retrieved collection %s",
                        self.address,
                        self.collection,
                        )

                return self
Пример #20
0
def _run_validation_inner(message, ):
    """
    Validates a message. Don't call this function directly;
    call validate(), above.

    Returns True iff the message is valid.

    message_id -- the primary key of an IncomingMessage
        that was generated by validate().
    """

    logger.info('%s: begin validation', message)

    try:
        key_id = message.key_id
    except ValueError:
        logger.warning('%s: message is unsigned; dropping', message)
        return False

    try:
        from kepi.trilby_api.models import Person
        actor = fetch(message.actor, Person)
    except json.decoder.JSONDecodeError as jde:
        logger.info('%s: invalid JSON; dropping: %s', message, jde)
        return False
    except UnicodeDecodeError:
        logger.info('%s: invalid UTF-8; dropping', message)
        return False

    if actor is None:
        logger.info('%s: remote actor does not exist; dropping message',
                    message)
        # FIXME: If this message is an instruction to delete a remote user,
        # it's valid if the remote user is Gone. Need to pass this out
        # from fetch() somehow.
        return False

    logger.debug('%s: message signature is: %s', message, message.signature)
    logger.debug('%s: message body is: %s', message, message.body)

    logger.debug('%s: actor details are: %s', message, actor)

    # XXX key used to sign must "_obviously_belong_to" the actor

    try:
        key = actor.publicKey
    except TypeError as te:
        logger.info(
            '%s: actor has an invalid public key (%s); dropping message',
            message,
            te,
        )
        return False

    logger.debug('Verifying; key=%s, path=%s, host=%s', key, message.path,
                 message.host)

    logger.debug(
        'All params: %s', {
            'headers': {
                'Content-Type': message.content_type,
                'Date': message.date,
                'Signature': message.signature,
                'Host': message.host,
                'Digest': message.digest,
            },
            'secret': key,
            'method': 'POST',
            'path': message.path,
            'host': message.host,
            'sign_header': 'Signature',
        })

    hv = HeaderVerifier(
        headers={
            'Content-Type': message.content_type,
            'Date': message.date,
            'Signature': message.signature,
            'Host': message.host,
            'Digest': message.digest,
        },
        secret=key,
        method='POST',
        path=message.path,
        host=message.host,
        sign_header='Signature',
    )

    if not hv.verify():
        logger.info('%s: spoofing attempt; message dropped', message)
        return False

    logger.debug('%s: validation passed!', message)

    return True
Пример #21
0
def on_note(fields, address):

    logger.debug("Looking up actor: %s",
            fields['attributedTo'])

    poster = sombrero_fetch.fetch(
        fields['attributedTo'],
        expected_type = trilby_models.Person,
        )

    if poster is None:
        logger.debug("  -- who does not exist")
        return None

    logger.debug("  -- who is %s", poster)

    if 'inReplyTo' in fields:
        in_reply_to = sombrero_fetch.fetch(
            fields['inReplyTo'],
            expected_type = trilby_models.Status,
            )
    else:
        in_reply_to = None

    is_sensitive = False # FIXME
    spoiler_text = '' # FIXME
    language = 'en' # FIXME

    visibility = _visibility_from_fields(
            fields)

    logger.debug('%s: creating status from %s',
        address,
        fields,
        )

    try:
        newbie = trilby_models.Status(
            remote_url = fields['id'],
            account = poster,
            in_reply_to = in_reply_to,
            content = fields['content'],
            sensitive = is_sensitive,
            spoiler_text = spoiler_text,
            visibility = visibility,
            language = language,
                )

        newbie.save()

        logger.debug('%s: created status %s',
            address,
            newbie,
            )

    except KeyError as ke:
        logger.debug('%s: missing field: %s',
            address,
            ke)
        return None

    except Exception as e:
        logger.debug('%s: failed to create status: %s',
            address,
            e)
        return None

    if 'tag' in fields:

        logger.debug('%s: adding tags', address)

        for tag in fields['tag']:

            if 'type' not in tag or 'href' not in tag:
                logger.debug('%s:  -- missing fields: %s',
                        address, tag)
                continue

            if tag['type'].lower() != 'mention':
                logger.debug('%s:  -- unknown tag type: %s',
                        address, tag)
                continue

            logger.debug('%s:   -- %s',
                    address, tag['href'])

            whom = sombrero_fetch.fetch(tag['href'],
                    expected_type = trilby_models.Person)

            if whom is None:
                logger.debug('%s:     -- not found',
                        address)
                continue

            mention = trilby_models.Mention(
                    status = newbie,
                    whom = whom,
                    )
            mention.save()

            logger.debug('%s:     -- %s',
                    address, mention)

        logger.debug('%s:   -- tags done',
                address)

    return newbie