marnanel-wip
Marnanel Thurman 2023-11-03 22:20:33 +00:00
rodzic f0ea18cd96
commit 0918382eae
2 zmienionych plików z 175 dodań i 34 usunięć

Wyświetl plik

@ -1,8 +1,10 @@
from socketserver import TCPServer
from fastcgi.core import FcgiHandler
import re
import json
from json import dumps
import logging
import kepi
import urllib.parse
from kepi.config import config, subcommand
logger = logging.getLogger('kepi.fastcgi')
@ -21,6 +23,10 @@ CONTENTTYPE_NODEINFO = (
USER_PAGE_RE = r'users/([a-z0-9-]+)/?'
HOST_META_URI = '/.well-known/host-meta'
NODEINFO_PART_1_URI = '/.well-known/nodeinfo'
NODEINFO_PART_2_URI = '/nodeinfo.json'
WEBFINGER_URI = '/.well-known/webfinger'
WEBFINGER_MIMETYPE = 'application/jrd+json; charset=utf-8'
ERROR_404 = """Content-Type: text/html
Status: 404 Not found
@ -78,35 +84,165 @@ def despatch_host_meta(env, match):
username = match.groups(1)
result = f"""Content-Type: {CONTENTTYPE_HOST_META}
Status: 200 OK
<?xml version="1.0" encoding="UTF-8"?>
result = {
'mimetype': CONTENTTYPE_HOST_META,
'text': """<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
<Link rel="lrdd" type="application/xrd+xml"
template="https://{env['SERVER_NAME']}/.well-known/webfinger?resource={{uri}}"/>
</XRD>"""
</XRD>""",
}
return result
def despatch_nodeinfo_part_1(env, match):
message = {
'links': [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": f"https://{env['SERVER_NAME']}/nodeinfo.json",
},
],
result = {
'mimetype': CONTENTTYPE_NODEINFO,
'json': {
'links': [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": f"https://{env['SERVER_NAME']}/nodeinfo.json",
},
],
},
}
result = f"""Content-Type: {CONTENTTYPE_NODEINFO}
Status: 200 OK
return result
"""+json.dumps(message)
def despatch_nodeinfo_part_2(env, match):
result = {
'mimetype': CONTENTTYPE_NODEINFO,
'json': {
"version": "2.0",
"software" : {
"name": "kepi",
"version": kepi.__version__,
},
"protocols": ['activitypub'],
"services": {
"inbound": [],
"outbound": [],
},
"openRegistrations": False,
"usage": {
"users": {
# When this information is meaningful,
# we can implement this more seriously.
"total": 1,
"activeMonth": 1,
},
"localPosts": 0,
"localComments": 0,
},
"metadata": {
},
},
}
return result
def despatch_webfinger(env, match):
user = urllib.parse.parse_qs(
qs = env['QUERY_STRING'],
).get('resource', '')
print("9100", user)
if not user:
return {
'status': 400,
'reason': 'No resource specified for webfinger',
}
user = user[0]
# Generally, user resources should be prefaced with "acct:",
# per RFC7565. We support this, but we don't enforce it.
user = re.sub(r'^acct:', '', user)
print("9110", user)
if '@' not in user:
return {
'status': 400,
'reason': 'Absolute name required',
}
username, hostname = user.split('@', 2)
if hostname not in [
env['SERVER_NAME'],
]:
return {
'status': 404,
'reason': 'Not local',
}
return {
'mimetype': WEBFINGER_MIMETYPE,
'json': {
"subject" : "acct:{}@{}".format(username, hostname),
"aliases" : [
actor_url,
],
"links":[
{
'rel': 'http://webfinger.net/rel/profile-page',
'type': 'text/html',
'href': actor_url,
},
# TODO
#{
# 'rel': 'http://schemas.google.com/g/2010#updates-from',
# 'type': 'application/atom+xml',
# 'href': 'FIXME',
#},
{
'rel': 'self',
'type': 'application/activity+json',
'href': actor_url,
},
{
'rel': 'http://ostatus.org/schema/1.0/subscribe',
'template': configured_url('AUTHORIZE_FOLLOW_LINK'),
},
]},
}
return result
def format_message_for_http(
mimetype = 'text/plain',
status = 200,
reason = 'OK',
text = None,
json = None,
):
if json is not None:
assert text is None
body = dumps(text, indent=2, sort_keys=True)
elif text is not None:
body = text
else:
assert mimetype=='text/plain'
body = reason
body = body.encode('UTF-8')
headers = (
f"Content-Type: {mimetype}\r\n"
f"Status: {status} {reason}\r\n"
f"Content-Length: {len(body)}\r\n"
"\r\n"
)
headers = headers.encode('UTF-8')
return headers+body
def despatch(env):
# XXX Here we check HTTP_ACCEPT
# XXX and DOCUMENT_URI and possibly QUERY_STRING
@ -128,9 +264,11 @@ def despatch(env):
print(env)
for regex, handler in [
(USER_PAGE_RE, despatch_user_page),
(HOST_META_URI, despatch_host_meta),
(NODEINFO_PART_1_URI, despatch_nodeinfo_part_1),
(USER_PAGE_RE, despatch_user_page),
(HOST_META_URI, despatch_host_meta),
(NODEINFO_PART_1_URI, despatch_nodeinfo_part_1),
(NODEINFO_PART_2_URI, despatch_nodeinfo_part_2),
(WEBFINGER_URI, despatch_webfinger),
]:
match = re.match(regex, uri)
@ -139,11 +277,12 @@ def despatch(env):
continue
result = handler(
env = env,
match = match,
)
env = env,
match = match,
)
if result is not None:
result = format_message_for_http(**result)
return result
return ERROR_404

Wyświetl plik

@ -26,7 +26,7 @@ WEBFINGER_URI = WEBFINGER_BASE_URI + '?resource={}'
WEBFINGER_MIMETYPE = 'application/jrd+json; charset=utf-8'
def call_despatch_and_parse_result(*args, **kwargs):
found = despatch(*args, **kwargs)
found = despatch(*args, **kwargs).decode('UTF-8')
result = email.message_from_string(found)
return result
@ -86,17 +86,18 @@ def test_fastcgi_nodeinfo_part_2():
env = {
'DOCUMENT_URI': NODEINFO_PART_2_URI,
'ACCEPT': NODEINFO_MIMETYPE,
'SERVER_NAME': 'wombles.example.org',
},
)
assert found['Status'].startswith('200 ')
assert found['Content-Type']==NODEINFO_MIMETYPE
response = json.loads(found.body)
response = json.loads(found.get_payload())
assert response['version']=='2.0'
assert response['software']['name']=='kepi'
assert 'activitypub' in response['software']['protocols']
assert 'activitypub' in response['protocols']
"""
From the original: we need to do this before the tests which get 200
@ -126,12 +127,14 @@ def test_fastcgi_webfinger():
):
uri = WEBFINGER_BASE_URI
if who is not None:
uri += f'?resource={who}'
who = f'resource={who}'
found = call_despatch_and_parse_result(
env = {
'DOCUMENT_URI': uri,
'DOCUMENT_URI': WEBFINGER_BASE_URI,
'ACCEPT': WEBFINGER_MIMETYPE,
'QUERY_STRING': who,
'SERVER_NAME': 'wombles.example.org',
},
)
@ -148,19 +151,18 @@ def test_fastcgi_webfinger():
expected_status = 400,
)
response = webfinger(
who = 'jamie@magic-torch.example.net',
expected_status = 400,
)
response = webfinger(
who = 'lord_lucan@altair.example.com',
expected_status = 404,
)
response = webfinger(
who = 'alice@testserver',
who = 'lord_lucan@wombles.example.org',
expected_status = 404,
)
response = webfinger(
who = 'alice@wombles.example.org',
expected_status = 200,
)