Exemple #1
0
def handle_inbound_accept_reject(activity, obj):
    '''
        Incoming Accept and Reject activites on Follow objects
        have side effects which are handled here
    '''

    actor = APObject.get_object_from_url(
        obj['actor'])  #actor on local server who is following external actor
    if not isinstance(actor, Actor):
        raise Exception(
            'Remote server tried to accept or reject a Follow request done by an object and not by an actor. Aborting.'
        )

    following = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == activity['actor']),
        Following.approved == 0).first()

    follow_activity = db.session.query(Follow).filter(
        db.and_(Follow.external_object_id == obj['object'],
                Follow.internal_actor_id == actor.id)).first()

    if following is None or follow_activity is None:
        return error('Follow request not found.', 404)

    if activity['type'] == 'Accept':
        following.approved = True
        db.session.add(following)
    else:
        db.session.delete(following)

    return None
Exemple #2
0
def route_user_outbox_paginated(actor_name, page):

    actor = db.session.query(Actor).filter_by(
        username=actor_name.lower()).first()

    if actor is None:
        return error('Actor not found', 404)

    activities = db.session.query(Activity).filter(
        db.and_(Activity.actor == actor,
                Activity.type != APObjectType.FOLLOW)).order_by(
                    Activity.published.desc()).paginate(page, 20).items
    api_url = config['api_url']

    orderedItems = []

    for activity in activities:
        orderedItems.append(activity.to_dict())

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{actor_name}/outbox/{page}',
        'partOf': f'{api_url}/actors/{actor_name}/outbox',
        'type': 'OrderedCollectionPage',
        'prev': f'{api_url}/actors/{actor_name}/outbox/{page-1}',
        'next': f'{api_url}/actors/{actor_name}/outbox/{page+1}',
        'orderedItems': orderedItems
    }

    response = make_response(output, 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
Exemple #3
0
def route_get_actor_following_page(username, page):
    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)

    items = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.approved == 1)).paginate(page, 20).items

    ordered_items = []

    for item in items:
        ordered_items.append(item.leader)

    return make_response(
        {
            '@context': 'https://www.w3.org/ns/activitystreams',
            'id':
            f'{config["api_url"]}/actors/{actor.username}/following/{page}',
            'type': 'OrderedCollectionPage',
            'totalItems': len(items),
            'partOf':
            f'{config["api_url"]}/actors/{actor.username}/following/',
            'orderedItems': ordered_items
        }, 200)
Exemple #4
0
def get_note_by_id(object_id):

    ap_object = db.session.query(APObject).filter(
        db.and_(APObject.id == object_id, APObject.external_id == None)).first()

    if ap_object is None:
        return error('Object not found', 404)
    response = make_response(ap_object.to_dict(), 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
Exemple #5
0
def handle_follow(inbound_json, actor, base_activity, base_object, is_local):
    '''
        Handles the various oddities associated with the correspondance nature of the Follow activity.
    '''
    leader = resolve_ap_object(inbound_json['object'])

    existing_follow = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == leader['id'])).first()

    if existing_follow is not None and existing_follow.approved is True:
        return error('You are already following this actor.')

    new_follow = Following(actor.id,
                           leader['id'],
                           leader['followers'],
                           approved=is_local)
    db.session.add(new_follow)

    # Due to the correspondance nature of the Follow activity, it has some very unusual requirements.
    # We need to detect if the incoming Follow activity is targeting a local user. If it is, we don't need
    # To deliver server-to-server messages about this transaction. Attempting to do so would cause problems
    # resulting from uncomitted database transactions and be a waste of resources.
    if is_local:
        local_leader = db.session.query(Actor).filter(
            db.func.lower(Actor.username) == db.func.lower(
                inbound_json['object'].replace(
                    f'{os.environ["API_URL"]}/actors/', ''))).first()
        if local_leader is None:
            return make_response('Actor not found', 404)
        actor_dict = actor.to_dict()
        new_followed_by = FollowedBy(
            local_leader.id,
            actor_dict['id'],
            actor_dict['inbox'],
            follower_shared_inbox=actor_dict['endpoints']['sharedInbox'],
            approved=True)
        db.session.add(
            Notification(
                local_leader,
                f'{actor_dict["preferredUsername"]} has followed you.',
                'Follow'))
        db.session.add(new_followed_by)
        db.session.commit()
    else:
        db.session.commit(
        )  # This is required so when we get an Accept activity back before the end of this request, we're able to find the Follow activity
        try:
            signed_request(actor, base_activity.to_dict(), leader['inbox'])
        except:
            return error(
                'Your follow request was not able to be delivered to that server.'
            )

    return make_response('', 200)
Exemple #6
0
def follow(actor, follow_activity):
    '''
        actor: Actor model
        follow_activity: Dictionary representation of the new follow request
    '''

    leader = resolve_ap_object(follow_activity['object'])

    existing_follow = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == leader['id'])).first()

    if existing_follow is not None:
        if existing_follow.approved is True:
            return error('You are already following this actor.')

        db.session.delete(existing_follow)

    new_activity = Follow()
    new_activity.set_actor(actor)
    new_activity.set_object(follow_activity['object'])
    db.session.add(new_activity)
    db.session.flush()

    new_follow = Following(actor.id, leader['id'], leader['followers'])
    db.session.add(new_follow)

    response = signed_request(actor, new_activity.to_dict(), leader['inbox'])

    db.session.commit()

    if response.status_code >= 400:

        return error('Something went wrong :(')

    return make_response('', 200)