Exemplo n.º 1
0
def get_or_create_remote_server(domain):
    """ get info on a remote server """
    try:
        return FederatedServer.objects.get(server_name=domain)
    except FederatedServer.DoesNotExist:
        pass

    try:
        data = get_data("https://%s/.well-known/nodeinfo" % domain)
        try:
            nodeinfo_url = data.get("links")[0].get("href")
        except (TypeError, KeyError):
            raise ConnectorException()

        data = get_data(nodeinfo_url)
        application_type = data.get("software", {}).get("name")
        application_version = data.get("software", {}).get("version")
    except ConnectorException:
        application_type = application_version = None

    server = FederatedServer.objects.create(
        server_name=domain,
        application_type=application_type,
        application_version=application_version,
    )
    return server
Exemplo n.º 2
0
def get_or_create_remote_server(domain, refresh=False):
    """get info on a remote server"""
    server = FederatedServer()
    try:
        server = FederatedServer.objects.get(server_name=domain)
        if not refresh:
            return server
    except FederatedServer.DoesNotExist:
        pass

    try:
        data = get_data(f"https://{domain}/.well-known/nodeinfo")
        try:
            nodeinfo_url = data.get("links")[0].get("href")
        except (TypeError, KeyError):
            raise ConnectorException()

        data = get_data(nodeinfo_url)
        application_type = data.get("software", {}).get("name")
        application_version = data.get("software", {}).get("version")
    except ConnectorException:
        if server.id:
            return server
        application_type = application_version = None

    server.server_name = domain
    server.application_type = application_type
    server.application_version = application_version

    server.save()
    return server
Exemplo n.º 3
0
def get_or_create_remote_server(domain):
    ''' get info on a remote server '''
    try:
        return FederatedServer.objects.get(server_name=domain)
    except FederatedServer.DoesNotExist:
        pass

    try:
        data = get_data('https://%s/.well-known/nodeinfo' % domain)
        try:
            nodeinfo_url = data.get('links')[0].get('href')
        except (TypeError, KeyError):
            raise ConnectorException()

        data = get_data(nodeinfo_url)
        application_type = data.get('software', {}).get('name')
        application_version = data.get('software', {}).get('version')
    except ConnectorException:
        application_type = application_version = None

    server = FederatedServer.objects.create(
        server_name=domain,
        application_type=application_type,
        application_version=application_version,
    )
    return server
Exemplo n.º 4
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
Exemplo n.º 5
0
def resolve_remote_id(
    remote_id, model=None, refresh=False, save=True, get_activity=False
):
    """take a remote_id and return an instance, creating if necessary"""
    if model:  # a bonus check we can do if we already know the model
        if isinstance(model, str):
            model = apps.get_model(f"bookwyrm.{model}", require_ready=True)
        result = model.find_existing_by_remote_id(remote_id)
        if result and not refresh:
            return result if not get_activity else result.to_activity_dataclass()

    # load the data and create the object
    try:
        data = get_data(remote_id)
    except ConnectorException:
        raise ActivitySerializerError(
            f"Could not connect to host for remote_id: {remote_id}"
        )
    # determine the model implicitly, if not provided
    # or if it's a model with subclasses like Status, check again
    if not model or hasattr(model.objects, "select_subclasses"):
        model = get_model_from_type(data.get("type"))

    # check for existing items with shared unique identifiers
    result = model.find_existing(data)
    if result and not refresh:
        return result if not get_activity else result.to_activity_dataclass()

    item = model.activity_serializer(**data)
    if get_activity:
        return item

    # if we're refreshing, "result" will be set and we'll update it
    return item.to_model(model=model, instance=result, save=save)
Exemplo n.º 6
0
def set_related_field(
    model_name, origin_model_name, related_field_name, related_remote_id, data
):
    """load reverse related fields (editions, attachments) without blocking"""
    model = apps.get_model(f"bookwyrm.{model_name}", require_ready=True)
    origin_model = apps.get_model(f"bookwyrm.{origin_model_name}", require_ready=True)

    if isinstance(data, str):
        existing = model.find_existing_by_remote_id(data)
        if existing:
            data = existing.to_activity()
        else:
            data = get_data(data)
    activity = model.activity_serializer(**data)

    # this must exist because it's the object that triggered this function
    instance = origin_model.find_existing_by_remote_id(related_remote_id)
    if not instance:
        raise ValueError(f"Invalid related remote id: {related_remote_id}")

    # set the origin's remote id on the activity so it will be there when
    # the model instance is created
    # edition.parentWork = instance, for example
    model_field = getattr(model, related_field_name)
    if hasattr(model_field, "activitypub_field"):
        setattr(activity, getattr(model_field, "activitypub_field"), instance.remote_id)
    item = activity.to_model(model=model)

    # if the related field isn't serialized (attachments on Status), then
    # we have to set it post-creation
    if not hasattr(model_field, "activitypub_field"):
        setattr(item, related_field_name, instance)
        item.save()
Exemplo n.º 7
0
def resolve_remote_id(remote_id, model=None, refresh=False, save=True):
    """ take a remote_id and return an instance, creating if necessary """
    if model:  # a bonus check we can do if we already know the model
        result = model.find_existing_by_remote_id(remote_id)
        if result and not refresh:
            return result

    # load the data and create the object
    try:
        data = get_data(remote_id)
    except ConnectorException:
        raise ActivitySerializerError(
            "Could not connect to host for remote_id in: %s" % (remote_id))
    # determine the model implicitly, if not provided
    if not model:
        model = get_model_from_type(data.get("type"))

    # check for existing items with shared unique identifiers
    result = model.find_existing(data)
    if result and not refresh:
        return result

    item = model.activity_serializer(**data)
    # if we're refreshing, "result" will be set and we'll update it
    return item.to_model(model=model, instance=result, save=save)
Exemplo n.º 8
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
Exemplo n.º 9
0
def subscribe_remote_webfinger(query):
    """get subscribe template from other servers"""
    template = None
    # usernames could be @user@domain or user@domain
    if not query:
        return WebFingerError("invalid_username")

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

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

    url = f"https://{domain}/.well-known/webfinger?resource=acct:{query}"

    try:
        data = get_data(url)
    except (ConnectorException, HTTPError):
        return WebFingerError("user_not_found")

    for link in data.get("links"):
        if link.get("rel") == "http://ostatus.org/schema/1.0/subscribe":
            template = link["template"]

    return template
Exemplo n.º 10
0
def get_remote_reviews(outbox):
    """ ingest reviews by a new remote bookwyrm user """
    outbox_page = outbox + "?page=true&type=Review"
    data = get_data(outbox_page)

    # TODO: pagination?
    for activity in data["orderedItems"]:
        if not activity["type"] == "Review":
            continue
        activitypub.Review(**activity).to_model()
Exemplo n.º 11
0
def get_remote_reviews(outbox):
    ''' ingest reviews by a new remote bookwyrm user '''
    outbox_page = outbox + '?page=true&type=Review'
    data = get_data(outbox_page)

    # TODO: pagination?
    for activity in data['orderedItems']:
        if not activity['type'] == 'Review':
            continue
        activitypub.Review(**activity).to_model(Review)
Exemplo n.º 12
0
def get_or_create_remote_server(domain):
    ''' get info on a remote server '''
    try:
        return FederatedServer.objects.get(server_name=domain)
    except FederatedServer.DoesNotExist:
        pass

    data = get_data('https://%s/.well-known/nodeinfo' % domain)

    try:
        nodeinfo_url = data.get('links')[0].get('href')
    except (TypeError, KeyError):
        return None

    data = get_data(nodeinfo_url)

    server = FederatedServer.objects.create(
        server_name=domain,
        application_type=data['software']['name'],
        application_version=data['software']['version'],
    )
    return server
Exemplo n.º 13
0
def resolve_remote_id(model, remote_id, refresh=False, save=True):
    ''' take a remote_id and return an instance, creating if necessary '''
    result = model.find_existing_by_remote_id(remote_id)
    if result and not refresh:
        return result

    # load the data and create the object
    try:
        data = get_data(remote_id)
    except (ConnectorException, ConnectionError):
        raise ActivitySerializerError(
            'Could not connect to host for remote_id in %s model: %s' % \
                (model.__name__, remote_id))

    # check for existing items with shared unique identifiers
    if not result:
        result = model.find_existing(data)
        if result and not refresh:
            return result

    item = model.activity_serializer(**data)
    # if we're refreshing, "result" will be set and we'll update it
    return item.to_model(model, instance=result, save=save)