Create some quick and dirty admin endpoints

This commit is contained in:
Reid 'arrdem' McKenzie 2022-11-26 19:26:39 -07:00
parent 35a37aab8a
commit e5b9b133fc
7 changed files with 90 additions and 13 deletions

View file

@ -4,4 +4,4 @@ cd "$(realpath $(dirname $0))"
bazel build ... bazel build ...
exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay.yaml) run exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay_prod.yaml) run

View file

@ -357,6 +357,7 @@ def relay_setup(obj: Application):
obj.config.listen = os.getenv("LISTEN_ADDRESS", obj.config.listen) obj.config.listen = os.getenv("LISTEN_ADDRESS", obj.config.listen)
obj.config.port = int(os.getenv("LISTEN_PORT", obj.config.port)) obj.config.port = int(os.getenv("LISTEN_PORT", obj.config.port))
obj.config.host = os.getenv("RELAY_HOSTNAME") obj.config.host = os.getenv("RELAY_HOSTNAME")
obj.config.admin_token = os.getenv("RELAY_ADMIN_TOKEN")
if not obj.config.host: if not obj.config.host:
click.echo("Error: No relay host configured! Set $RELAY_HOSTNAME") click.echo("Error: No relay host configured! Set $RELAY_HOSTNAME")
exit(1) exit(1)

View file

@ -78,6 +78,7 @@ class RelayConfig(DotDict):
"json": 1024, "json": 1024,
"objects": 1024, "objects": 1024,
"digests": 1024, "digests": 1024,
"admin_token": None,
} }
) )

View file

@ -6,6 +6,7 @@ import logging
import socket import socket
from urllib.parse import urlparse from urllib.parse import urlparse
import uuid import uuid
from typing import Union
from Crypto.Hash import SHA, SHA256, SHA512 from Crypto.Hash import SHA, SHA256, SHA512
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
@ -453,7 +454,7 @@ class Message(DotDict):
class Response(AiohttpResponse): class Response(AiohttpResponse):
@classmethod @classmethod
def new(cls, body="", status=200, headers=None, ctype="text"): def new(cls, body: Union[dict, str, bytes] = "", status=200, headers=None, ctype="text"):
kwargs = { kwargs = {
"status": status, "status": status,
"headers": headers, "headers": headers,

View file

@ -1,6 +1,6 @@
import logging import logging
from aiohttp.web import HTTPUnauthorized from aiohttp.web import HTTPUnauthorized, Request
from relay import __version__, misc from relay import __version__, misc
from relay.http_debug import STATS from relay.http_debug import STATS
from relay.misc import ( from relay.misc import (
@ -148,7 +148,8 @@ async def inbox(request):
@register_route("GET", "/.well-known/webfinger") @register_route("GET", "/.well-known/webfinger")
async def webfinger(request): async def webfinger(request):
subject = request.query["resource"] if not (subject := request.query.get("resource")):
return Response.new_error(404, "no resource specified", "json")
if subject != f"acct:relay@{request.app.config.host}": if subject != f"acct:relay@{request.app.config.host}":
return Response.new_error(404, "user not found", "json") return Response.new_error(404, "user not found", "json")
@ -203,5 +204,73 @@ async def nodeinfo_wellknown(request):
@register_route("GET", "/stats") @register_route("GET", "/stats")
async def stats(*args, **kwargs): async def stats(request):
return Response.new(STATS, ctype="json") stats = STATS.copy()
stats["pending_requests"] = len(request.app.database.get("follow-requests", {}))
return Response.new(stats, ctype="json")
@register_route("POST", "/admin/config")
async def set_config(request: Request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
# FIXME: config doesn't have a way to go from JSON or update, using dict stuff
new_config = await request.json()
request.app.config.update(new_config)
# If there are pending follows which are NOW whitelisted, allow them
if request.app.config.whitelist_enabled:
for domain in request.app.config.whitelist:
if (pending_follow := request.app.database.get_request(domain, False)):
await misc.request(
actor.shared_inbox,
misc.Message.new_response(
host=request.app.config.host,
actor=pending_follow["actor"],
followid=pending_follow["followid"],
accept=True
),
)
await misc.request(
actor.shared_inbox,
misc.Message.new_follow(
host=request.app.config.host,
actor=pending_follow["actor"]
),
)
request.app.database.del_request(domain)
request.app.database.save()
request.app.config.save()
return Response.new(status=202)
@register_route("GET", "/admin/config")
def set_config(request: Request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
return Response.new(request.app.config, status=200, ctype="json")
@register_route("GET", "/admin/pending")
def get_pending(request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
return Response.new(request.app.database["follow-requests"], status=200, ctype="json")

View file

@ -4,12 +4,8 @@
ichor entrypoint ichor entrypoint
""" """
from ichor.bootstrap import (
BOOTSTRAP,
NOT1,
TRUE,
)
from ichor import isa from ichor import isa
from ichor.bootstrap import BOOTSTRAP, NOT1, TRUE
from ichor.interpreter import Interpreter from ichor.interpreter import Interpreter

View file

@ -5,6 +5,7 @@
from abc import ABC from abc import ABC
from dataclasses import dataclass from dataclasses import dataclass
import typing as t import typing as t
from uuid import UUID
from shoggoth.types import Keyword, List, Symbol from shoggoth.types import Keyword, List, Symbol
@ -85,12 +86,20 @@ class Multiverse:
""" """
soup: t.Dict[]
@dataclass @dataclass
class Namespace: class Namespace:
pass pass
@dataclass
class Entry:
ns: "Namespace"
expr: t.Any
value: t.Any
soup: t.Dict[UUID, Entry] = {}
BOOTSTRAP = "lang.shoggoth.v0.bootstrap" BOOTSTRAP = "lang.shoggoth.v0.bootstrap"
SPECIALS = Multiverse.Namespace(Symbol(BOOTSTRAP), { SPECIALS = Multiverse.Namespace(Symbol(BOOTSTRAP), {