kopia lustrzana https://gitlab.com/marnanel/chapeau
Intermediate
rodzic
f0ea18cd96
commit
0918382eae
183
kepi/fastcgi.py
183
kepi/fastcgi.py
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue