예제 #1
0
def has_valid_signature(request, activity):
    """verify incoming signature"""
    try:
        signature = Signature.parse(request)

        key_actor = urldefrag(signature.key_id).url
        if key_actor != activity.get("actor"):
            raise ValueError("Wrong actor created signature.")

        remote_user = activitypub.resolve_remote_id(key_actor, model=models.User)
        if not remote_user:
            return False

        try:
            signature.verify(remote_user.key_pair.public_key, request)
        except ValueError:
            old_key = remote_user.key_pair.public_key
            remote_user = activitypub.resolve_remote_id(
                remote_user.remote_id, model=models.User, refresh=True
            )
            if remote_user.key_pair.public_key == old_key:
                raise  # Key unchanged.
            signature.verify(remote_user.key_pair.public_key, request)
    except (ValueError, requests.exceptions.HTTPError):
        return False
    return True
예제 #2
0
 def get_or_create_book(self, remote_id):
     edition = activitypub.resolve_remote_id(remote_id,
                                             model=models.Edition)
     work = edition.parent_work
     work.default_edition = work.get_default_edition()
     work.save()
     return edition
예제 #3
0
def handle_remote_webfinger(query):
    ''' webfingerin' other servers '''
    user = None

    # usernames could be @user@domain or user@domain
    if not query:
        return None

    if query[0] == '@':
        query = query[1:]

    try:
        domain = query.split('@')[1]
    except IndexError:
        return None

    try:
        user = models.User.objects.get(username=query)
    except models.User.DoesNotExist:
        url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \
            (domain, query)
        try:
            data = get_data(url)
        except (ConnectorException, HTTPError):
            return None

        for link in data.get('links'):
            if link.get('rel') == 'self':
                try:
                    user = activitypub.resolve_remote_id(
                        models.User, link['href']
                    )
                except KeyError:
                    return None
    return user
예제 #4
0
    def set_field_from_activity(self, instance, data, overwrite=True):
        if not overwrite:
            return False

        original = getattr(instance, self.name)
        to = data.to
        cc = data.cc

        # we need to figure out who this is to get their followers link
        for field in ["attributedTo", "owner", "actor"]:
            if hasattr(data, field):
                user_field = field
                break
        if not user_field:
            raise ValidationError("No user field found for privacy", data)
        user = activitypub.resolve_remote_id(getattr(data, user_field), model="User")

        if to == [self.public]:
            setattr(instance, self.name, "public")
        elif to == [user.followers_url]:
            setattr(instance, self.name, "followers")
        elif cc == []:
            setattr(instance, self.name, "direct")
        elif self.public in cc:
            setattr(instance, self.name, "unlisted")
        else:
            setattr(instance, self.name, "followers")
        return original == getattr(instance, self.name)
예제 #5
0
파일: status.py 프로젝트: arkhi/bookwyrm
    def ignore_activity(cls, activity):  # pylint: disable=too-many-return-statements
        """keep notes if they are replies to existing statuses"""
        if activity.type == "Announce":
            try:
                boosted = activitypub.resolve_remote_id(activity.object,
                                                        get_activity=True)
            except activitypub.ActivitySerializerError:
                # if we can't load the status, definitely ignore it
                return True
            # keep the boost if we would keep the status
            return cls.ignore_activity(boosted)

        # keep if it if it's a custom type
        if activity.type != "Note":
            return False
        # keep it if it's a reply to an existing status
        if cls.objects.filter(remote_id=activity.inReplyTo).exists():
            return False

        # keep notes if they mention local users
        if activity.tag == MISSING or activity.tag is None:
            return True
        tags = [l["href"] for l in activity.tag if l["type"] == "Mention"]
        user_model = apps.get_model("bookwyrm.User", require_ready=True)
        for tag in tags:
            if user_model.objects.filter(remote_id=tag, local=True).exists():
                # we found a mention of a known use boost
                return False
        return True
예제 #6
0
def handle_remote_webfinger(query):
    """webfingerin' other servers"""
    user = None

    # usernames could be @user@domain or user@domain
    if not query:
        return None

    if query[0] == "@":
        query = query[1:]

    try:
        domain = query.split("@")[1]
    except IndexError:
        return None

    try:
        user = models.User.objects.get(username__iexact=query)
    except models.User.DoesNotExist:
        url = "https://%s/.well-known/webfinger?resource=acct:%s" % (domain,
                                                                     query)
        try:
            data = get_data(url)
        except (ConnectorException, HTTPError):
            return None

        for link in data.get("links"):
            if link.get("rel") == "self":
                try:
                    user = activitypub.resolve_remote_id(link["href"],
                                                         model=models.User)
                except (KeyError, activitypub.ActivitySerializerError):
                    return None
    return user
예제 #7
0
def handle_follow_reject(activity):
    ''' someone is rejecting a follow request '''
    requester = models.User.objects.get(remote_id=activity['object']['actor'])
    rejecter = activitypub.resolve_remote_id(models.User, activity['actor'])

    request = models.UserFollowRequest.objects.get(user_subject=requester,
                                                   user_object=rejecter)
    request.delete()
예제 #8
0
def handle_unfollow(activity):
    ''' unfollow a local user '''
    obj = activity['object']
    requester = activitypub.resolve_remote_id(models.User, obj['actor'])
    to_unfollow = models.User.objects.get(remote_id=obj['object'])
    # raises models.User.DoesNotExist

    to_unfollow.followers.remove(requester)
예제 #9
0
 def field_from_activity(self, value):
     items = []
     for remote_id in value:
         try:
             validate_remote_id(remote_id)
         except ValidationError:
             continue
         items.append(
             activitypub.resolve_remote_id(self.related_model, remote_id)
         )
     return items
예제 #10
0
 def field_from_activity(self, value):
     items = []
     if value is None or value is MISSING:
         return []
     for remote_id in value:
         try:
             validate_remote_id(remote_id)
         except ValidationError:
             continue
         items.append(
             activitypub.resolve_remote_id(remote_id, model=self.related_model)
         )
     return items
예제 #11
0
def handle_follow_accept(activity):
    ''' hurray, someone remote accepted a follow request '''
    # figure out who they want to follow
    requester = models.User.objects.get(remote_id=activity['object']['actor'])
    # figure out who they are
    accepter = activitypub.resolve_remote_id(models.User, activity['actor'])

    try:
        request = models.UserFollowRequest.objects.get(user_subject=requester,
                                                       user_object=accepter)
        request.delete()
    except models.UserFollowRequest.DoesNotExist:
        pass
    accepter.followers.add(requester)
예제 #12
0
 def field_from_activity(self, value):
     if not isinstance(value, list):
         return None
     items = []
     for link_json in value:
         link = activitypub.Link(**link_json)
         tag_type = link.type if link.type != 'Mention' else 'Person'
         if tag_type != self.related_model.activity_serializer.type:
             # tags can contain multiple types
             continue
         items.append(
             activitypub.resolve_remote_id(self.related_model, link.href)
         )
     return items
예제 #13
0
 def field_from_activity(self, value):
     if value is None or value is MISSING:
         return None
     if not isinstance(value, list):
         # If this is a link, we currently aren't doing anything with it
         return None
     items = []
     for remote_id in value:
         try:
             validate_remote_id(remote_id)
         except ValidationError:
             continue
         items.append(
             activitypub.resolve_remote_id(remote_id, model=self.related_model)
         )
     return items
예제 #14
0
    def field_from_activity(self, value):
        if not value:
            return None

        related_model = self.related_model
        if isinstance(value, dict) and value.get('id'):
            # this is an activitypub object, which we can deserialize
            activity_serializer = related_model.activity_serializer
            return activity_serializer(**value).to_model(related_model)
        try:
            # make sure the value looks like a remote id
            validate_remote_id(value)
        except ValidationError:
            # we don't know what this is, ignore it
            return None
        # gets or creates the model field from the remote id
        return activitypub.resolve_remote_id(related_model, value)
예제 #15
0
    def field_from_activity(self, value):
        if not value:
            return None

        related_model = self.related_model
        if hasattr(value, "id") and value.id:
            if not self.load_remote:
                # only look in the local database
                return related_model.find_existing(value.serialize())
            # this is an activitypub object, which we can deserialize
            return value.to_model(model=related_model)
        try:
            # make sure the value looks like a remote id
            validate_remote_id(value)
        except ValidationError:
            # we don't know what this is, ignore it
            return None
        # gets or creates the model field from the remote id
        if not self.load_remote:
            # only look in the local database
            return related_model.find_existing_by_remote_id(value)
        return activitypub.resolve_remote_id(value, model=related_model)
예제 #16
0
 def get_or_create_book(self, remote_id):
     return activitypub.resolve_remote_id(remote_id, model=models.Edition)