kopia lustrzana https://gitlab.com/jaywink/federation
Add support for AS2 document webfinger rel used by Mastodon et al
Also fix reference to RFC by renaming RFC3033 to RFC7033 :Pmerge-requests/132/head
rodzic
865636c65b
commit
ceb5d0446e
|
|
@ -16,7 +16,7 @@
|
|||
* The `id` and possible `target_id` are now either URL format identifiers (ActivityPub) or a handle or GUID (Diaspora, depending on entity). Additionally a new `actor_id` has been added which for ActivityPub is an URL and for Diaspora a handle. Note, Diaspora entities always have also the `guid`, `handle`, `target_guid` and `target_handle` as before v0.15.0, depending on the entity. When creating Diaspora entities, you must pass these in for sending to work.
|
||||
* The high level `fetchers.retrieve_remote_content` signature has changed. It now expects an `id` for fetching from AP protocol and `handle`, `guid` and `entity_type` to fetch from Diaspora. Additionally a `sender_key_fetcher` can be passed in as before to optimize public key fetching using a callable.
|
||||
* The high level `fetchers.retrieve_remote_profile` signature has changed. It now expects an `id` for fetching from AP protocol and `handle` for fetching from Diaspora. Additionally a `sender_key_fetcher` can be passed in as before to optimize public key fetching using a callable.
|
||||
* The generator class `RFC3033Webfinger` now expects instead of an `id` the `handle` and `guid` of the profile.
|
||||
* The generator class `RFC7033Webfinger` now expects instead of an `id` the `handle` and `guid` of the profile.
|
||||
* NodeInfo2 parser now returns the admin user in `handle` format instead of a Diaspora format URL.
|
||||
* The high level inbound and outbound functions `inbound.handle_receive`, `outbound.handle_send` parameter `user` must now receive a `UserType` compatible object. This must have the attributes `id` and `private_key`. If Diaspora support is required then also `handle` and `guid` should exist. The type can be found as a class in `types.UserType`.
|
||||
* The outbound function `outbound.handle_send` parameter `recipients` structure has changed. It must now for Diaspora contain either a `handle` (public delivery) or tuple of `handle, RSAPublicKey, guid` for private delivery. For AP delivery either `url ID` for public delivery or tuple of `url ID, RSAPublicKey` for private delivery.
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
JSON encrypted payload encryption and decryption is handled by the Diaspora `EncryptedPayload` class.
|
||||
|
||||
* Add RFC3033 webfinger generator ([related issue](https://github.com/jaywink/federation/issues/108))
|
||||
* Add RFC7033 webfinger generator ([related issue](https://github.com/jaywink/federation/issues/108))
|
||||
|
||||
Also provided is a Django view and url configuration for easy addition into Django projects. Django is not a hard dependency of this library, usage of the Django view obviously requires installing Django itself. For configuration details see documentation.
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ Generator classes
|
|||
.. autoclass:: federation.hostmeta.generators.DiasporaWebFinger
|
||||
.. autoclass:: federation.hostmeta.generators.DiasporaHCard
|
||||
.. autoclass:: federation.hostmeta.generators.NodeInfo
|
||||
.. autoclass:: federation.hostmeta.generators.RFC3033Webfinger
|
||||
.. autoclass:: federation.hostmeta.generators.RFC7033Webfinger
|
||||
.. autoclass:: federation.hostmeta.generators.SocialRelayWellKnown
|
||||
|
||||
Fetchers
|
||||
|
|
@ -98,7 +98,7 @@ Some ready provided views and URL configuration exist for Django.
|
|||
|
||||
Note! Django is not part of the normal requirements for this library. It must be installed separately.
|
||||
|
||||
.. autofunction:: federation.hostmeta.django.generators.rfc3033_webfinger_view
|
||||
.. autofunction:: federation.hostmeta.django.generators.rfc7033_webfinger_view
|
||||
.. autofunction:: federation.hostmeta.django.generators.nodeinfo2_view
|
||||
|
||||
Configuration
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
from .generators import rfc3033_webfinger_view
|
||||
from .generators import rfc7033_webfinger_view
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import logging
|
|||
|
||||
from django.http import HttpResponseBadRequest, JsonResponse, HttpResponseNotFound
|
||||
|
||||
from federation.hostmeta.generators import RFC3033Webfinger, generate_nodeinfo2_document
|
||||
from federation.hostmeta.generators import RFC7033Webfinger, generate_nodeinfo2_document
|
||||
from federation.utils.django import get_configuration, get_function_from_config
|
||||
from federation.utils.text import get_path_from_url
|
||||
|
||||
|
|
@ -19,9 +19,9 @@ def nodeinfo2_view(request, *args, **kwargs):
|
|||
return JsonResponse(generate_nodeinfo2_document(**nodeinfo2))
|
||||
|
||||
|
||||
def rfc3033_webfinger_view(request, *args, **kwargs):
|
||||
def rfc7033_webfinger_view(request, *args, **kwargs):
|
||||
"""
|
||||
Django view to generate an RFC3033 webfinger.
|
||||
Django view to generate an RFC7033 webfinger.
|
||||
"""
|
||||
resource = request.GET.get("resource")
|
||||
if not resource:
|
||||
|
|
@ -35,13 +35,12 @@ def rfc3033_webfinger_view(request, *args, **kwargs):
|
|||
try:
|
||||
profile = profile_func(handle=handle, request=request)
|
||||
except Exception as exc:
|
||||
logger.warning("rfc3033_webfinger_view - Failed to get profile by handle %s: %s", handle, exc)
|
||||
logger.warning("rfc7033_webfinger_view - Failed to get profile by handle %s: %s", handle, exc)
|
||||
return HttpResponseNotFound()
|
||||
|
||||
profile = profile.as_protocol("diaspora")
|
||||
|
||||
config = get_configuration()
|
||||
webfinger = RFC3033Webfinger(
|
||||
webfinger = RFC7033Webfinger(
|
||||
id=profile.id,
|
||||
handle=profile.handle,
|
||||
guid=profile.guid,
|
||||
base_url=config.get('base_url'),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from federation.hostmeta.django import rfc3033_webfinger_view
|
||||
from federation.hostmeta.django import rfc7033_webfinger_view
|
||||
from federation.hostmeta.django.generators import nodeinfo2_view
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^.well-known/webfinger$', rfc3033_webfinger_view, name="rfc3033-webfinger"),
|
||||
url(r'^.well-known/webfinger$', rfc7033_webfinger_view, name="rfc7033-webfinger"),
|
||||
url(r'^.well-known/x-nodeinfo2$', nodeinfo2_view, name="nodeinfo2"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ from jsonschema import validate
|
|||
from jsonschema.exceptions import ValidationError
|
||||
from xrd import XRD, Link, Element
|
||||
|
||||
from federation.utils.diaspora import parse_profile_diaspora_id
|
||||
|
||||
|
||||
def generate_host_meta(template=None, *args, **kwargs):
|
||||
"""Generate a host-meta XRD document.
|
||||
|
|
@ -333,13 +331,15 @@ def get_nodeinfo_well_known_document(url, document_path=None):
|
|||
}
|
||||
|
||||
|
||||
class RFC3033Webfinger:
|
||||
class RFC7033Webfinger:
|
||||
"""
|
||||
RFC 3033 webfinger - see https://diaspora.github.io/diaspora_federation/discovery/webfinger.html
|
||||
RFC 7033 webfinger - see https://tools.ietf.org/html/rfc7033
|
||||
|
||||
A Django view is also available, see the child ``django`` module for view and url configuration.
|
||||
|
||||
:param id: Diaspora ID in URI format
|
||||
:param id: Profile ActivityPub ID in URL format
|
||||
:param handle: Profile Diaspora handle
|
||||
:param guid: Profile Diaspora guid
|
||||
:param base_url: The base URL of the server (protocol://domain.tld)
|
||||
:param profile_path: Profile path for the user (for example `/profile/johndoe/`)
|
||||
:param hcard_path: (Optional) hCard path, defaults to ``/hcard/users/``.
|
||||
|
|
@ -347,8 +347,10 @@ class RFC3033Webfinger:
|
|||
:returns: dict
|
||||
"""
|
||||
def __init__(
|
||||
self, handle, guid, base_url, profile_path, hcard_path="/hcard/users/", atom_path=None, search_path=None,
|
||||
self, id: str, handle: str, guid: str, base_url: str, profile_path: str, hcard_path: str="/hcard/users/",
|
||||
atom_path: str=None, search_path: str=None,
|
||||
):
|
||||
self.id = id
|
||||
self.handle = handle
|
||||
self.guid = guid
|
||||
self.base_url = base_url
|
||||
|
|
@ -360,6 +362,10 @@ class RFC3033Webfinger:
|
|||
def render(self):
|
||||
webfinger = {
|
||||
"subject": "acct:%s" % self.handle,
|
||||
"aliases": [
|
||||
f"{self.base_url}{self.profile_path}",
|
||||
self.id,
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
|
|
@ -380,6 +386,11 @@ class RFC3033Webfinger:
|
|||
"rel": "salmon",
|
||||
"href": "%s/receive/users/%s" % (self.base_url, self.guid),
|
||||
},
|
||||
{
|
||||
"rel": "self",
|
||||
"href": self.id,
|
||||
"type": "application/activity+json",
|
||||
},
|
||||
],
|
||||
}
|
||||
if self.atom_path:
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
from federation.entities.base import Profile
|
||||
|
||||
|
||||
def get_object_function(object_id):
|
||||
def dummy_profile():
|
||||
return Profile(
|
||||
url=f"https://example.com/profile/1234/",
|
||||
atom_url=f"https://example.com/profile/1234/atom.xml",
|
||||
id=f"https://example.com/profile/1234/",
|
||||
id=f"https://example.com/p/1234/",
|
||||
handle="foobar@example.com",
|
||||
guid="1234",
|
||||
name="Bob Bobértson",
|
||||
)
|
||||
|
||||
|
||||
def get_object_function(object_id):
|
||||
return dummy_profile()
|
||||
|
||||
|
||||
def get_profile(handle=None, request=None):
|
||||
return Profile(
|
||||
url=f"https://example.com/profile/1234/",
|
||||
atom_url=f"https://example.com/profile/1234/atom.xml",
|
||||
id=f"https://example.com/profile/1234/",
|
||||
handle="foobar@example.com",
|
||||
guid="1234",
|
||||
)
|
||||
return dummy_profile()
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ from unittest.mock import patch, Mock
|
|||
|
||||
from django.test import RequestFactory
|
||||
|
||||
from federation.entities.base import Profile
|
||||
from federation.hostmeta.django import rfc3033_webfinger_view
|
||||
from federation.hostmeta.django import rfc7033_webfinger_view
|
||||
from federation.hostmeta.django.generators import nodeinfo2_view
|
||||
from federation.utils.django import get_function_from_config
|
||||
from federation.tests.fixtures.hostmeta import NODEINFO2_10_DOC
|
||||
|
|
@ -29,31 +28,35 @@ class TestNodeInfo2View:
|
|||
assert response.status_code == 200
|
||||
|
||||
|
||||
class TestRFC3033WebfingerView:
|
||||
class TestRFC7033WebfingerView:
|
||||
def test_no_resource_returns_bad_request(self):
|
||||
request = RequestFactory().get("/.well-known/webfinger")
|
||||
response = rfc3033_webfinger_view(request)
|
||||
response = rfc7033_webfinger_view(request)
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_invalid_resource_returns_bad_request(self):
|
||||
request = RequestFactory().get("/.well-known/webfinger?resource=foobar")
|
||||
response = rfc3033_webfinger_view(request)
|
||||
response = rfc7033_webfinger_view(request)
|
||||
assert response.status_code == 400
|
||||
|
||||
@patch("federation.hostmeta.django.generators.get_function_from_config")
|
||||
def test_unknown_handle_returns_not_found(self, mock_get_func):
|
||||
mock_get_func.return_value = Mock(side_effect=Exception)
|
||||
request = RequestFactory().get("/.well-known/webfinger?resource=acct:foobar@domain.tld")
|
||||
response = rfc3033_webfinger_view(request)
|
||||
response = rfc7033_webfinger_view(request)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_rendered_webfinger_returned(self):
|
||||
request = RequestFactory().get("/.well-known/webfinger?resource=acct:foobar@example.com")
|
||||
response = rfc3033_webfinger_view(request)
|
||||
response = rfc7033_webfinger_view(request)
|
||||
assert response.status_code == 200
|
||||
assert response['Content-Type'] == "application/jrd+json"
|
||||
assert json.loads(response.content.decode("utf-8")) == {
|
||||
"subject": "acct:foobar@example.com",
|
||||
"aliases": [
|
||||
"https://example.com/profile/1234/",
|
||||
"https://example.com/p/1234/",
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
|
|
@ -75,51 +78,9 @@ class TestRFC3033WebfingerView:
|
|||
"href": "https://example.com/receive/users/1234",
|
||||
},
|
||||
{
|
||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||
"type": "application/atom+xml",
|
||||
"href": "https://example.com/profile/1234/atom.xml",
|
||||
},
|
||||
{
|
||||
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template": "https://example.com/search?q={uri}",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@patch("federation.hostmeta.django.generators.get_function_from_config", autospec=True)
|
||||
def test_rendered_webfinger_returned__non_diaspora_id(self, mock_get_config):
|
||||
mock_get_config.return_value = Mock(return_value=Profile(
|
||||
url="https://example.com/profile/1234/",
|
||||
atom_url="https://example.com/profile/1234/atom.xml",
|
||||
handle="foobar@example.com",
|
||||
guid="1234",
|
||||
id="https://example.com/profile/1234",
|
||||
))
|
||||
request = RequestFactory().get("/.well-known/webfinger?resource=acct:foobar@example.com")
|
||||
response = rfc3033_webfinger_view(request)
|
||||
assert response.status_code == 200
|
||||
assert response['Content-Type'] == "application/jrd+json"
|
||||
assert json.loads(response.content.decode("utf-8")) == {
|
||||
"subject": "acct:foobar@example.com",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "https://example.com/hcard/users/1234",
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "https://example.com",
|
||||
},
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": "https://example.com/profile/1234/",
|
||||
},
|
||||
{
|
||||
"rel": "salmon",
|
||||
"href": "https://example.com/receive/users/1234",
|
||||
"rel": "self",
|
||||
"href": "https://example.com/p/1234/",
|
||||
"type": "application/activity+json",
|
||||
},
|
||||
{
|
||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from federation.hostmeta.generators import (
|
||||
generate_host_meta, generate_legacy_webfinger, generate_hcard,
|
||||
SocialRelayWellKnown, NodeInfo, get_nodeinfo_well_known_document, RFC3033Webfinger,
|
||||
SocialRelayWellKnown, NodeInfo, get_nodeinfo_well_known_document, RFC7033Webfinger,
|
||||
generate_nodeinfo2_document)
|
||||
from federation.tests.fixtures.payloads import DIASPORA_HOSTMETA, DIASPORA_WEBFINGER
|
||||
|
||||
|
|
@ -223,9 +223,10 @@ class TestNodeInfoGenerator:
|
|||
assert wellknown["links"][0]["href"] == "https://example.com/nodeinfo/1.0"
|
||||
|
||||
|
||||
class TestRFC3033Webfinger:
|
||||
def test_rfc3033_webfinger__all_properties(self):
|
||||
webfinger = RFC3033Webfinger(
|
||||
class TestRFC7033Webfinger:
|
||||
def test_rfc7033_webfinger__all_properties(self):
|
||||
webfinger = RFC7033Webfinger(
|
||||
id="https://example.com/p/1234/",
|
||||
handle="foobar@example.com",
|
||||
guid="1234",
|
||||
base_url="https://example.com",
|
||||
|
|
@ -236,6 +237,10 @@ class TestRFC3033Webfinger:
|
|||
).render()
|
||||
assert webfinger == {
|
||||
"subject": "acct:foobar@example.com",
|
||||
"aliases": [
|
||||
"https://example.com/profile/1234/",
|
||||
"https://example.com/p/1234/",
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
|
|
@ -256,6 +261,11 @@ class TestRFC3033Webfinger:
|
|||
"rel": "salmon",
|
||||
"href": "https://example.com/receive/users/1234",
|
||||
},
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "https://example.com/p/1234/",
|
||||
"type": "application/activity+json",
|
||||
},
|
||||
{
|
||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||
"type": "application/atom+xml",
|
||||
|
|
@ -268,8 +278,9 @@ class TestRFC3033Webfinger:
|
|||
],
|
||||
}
|
||||
|
||||
def test_rfc3033_webfinger__minimal(self):
|
||||
webfinger = RFC3033Webfinger(
|
||||
def test_rfc7033_webfinger__minimal(self):
|
||||
webfinger = RFC7033Webfinger(
|
||||
id="https://example.com/p/1234/",
|
||||
handle="foobar@example.com",
|
||||
guid="1234",
|
||||
base_url="https://example.com",
|
||||
|
|
@ -277,6 +288,10 @@ class TestRFC3033Webfinger:
|
|||
).render()
|
||||
assert webfinger == {
|
||||
"subject": "acct:foobar@example.com",
|
||||
"aliases": [
|
||||
"https://example.com/profile/1234/",
|
||||
"https://example.com/p/1234/",
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
|
|
@ -297,5 +312,10 @@ class TestRFC3033Webfinger:
|
|||
"rel": "salmon",
|
||||
"href": "https://example.com/receive/users/1234",
|
||||
},
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "https://example.com/p/1234/",
|
||||
"type": "application/activity+json",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue