From 73430848e824ec56bcb879dde2027435f24cf5af Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Fri, 6 Oct 2023 19:14:14 +0530 Subject: [PATCH] feat: init ap server w webfinger endpoint --- ap/__init__.py | 0 ap/__main__.py | 12 ++++++++++++ ap/config.py | 12 ++++++++++++ ap/db.py | 26 +++++++++++++++++++++++++ ap/errors.py | 17 ++++++++++++++++ ap/server.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 ap/__init__.py create mode 100644 ap/__main__.py create mode 100644 ap/config.py create mode 100644 ap/db.py create mode 100644 ap/errors.py create mode 100644 ap/server.py diff --git a/ap/__init__.py b/ap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ap/__main__.py b/ap/__main__.py new file mode 100644 index 0000000..da39b3b --- /dev/null +++ b/ap/__main__.py @@ -0,0 +1,12 @@ +if __name__ == "__main__": + from .config import Config + from .db import Database + from .server import app + + db = Database() + db.add_user("alice") + + config = Config() + config.options["port"] = 9003 + config.options["hostname"] = f'lab.batsense.net:{config.options["port"]}' + app.run(host="0.0.0.0", port=config.options["port"], debug=True) diff --git a/ap/config.py b/ap/config.py new file mode 100644 index 0000000..8ed8810 --- /dev/null +++ b/ap/config.py @@ -0,0 +1,12 @@ +from ftest_common.logger import logger + + +class Config(object): + _instance = None + options = {} + + def __new__(cls): + if cls._instance is None: + logger.info("Creating the config") + cls._instance = super(Config, cls).__new__(cls) + return cls._instance diff --git a/ap/db.py b/ap/db.py new file mode 100644 index 0000000..0c2f4f1 --- /dev/null +++ b/ap/db.py @@ -0,0 +1,26 @@ +from ftest_common.logger import logger + + +class Database(object): + _instance = None + db = {} + # { "user": [{name: foo..}{..}{..}] } + + def __new__(cls): + if cls._instance is None: + logger.info("Creating the store") + cls._instance = super(Database, cls).__new__(cls) + return cls._instance + + def get_user(self, username): + for u in self.db["users"]: + print(u) + if u["username"] == username: + return u + return None + + def add_user(self, username): + if "users" not in self.db: + self.db["users"] = [] + + self.db["users"].append({"username": username}) diff --git a/ap/errors.py b/ap/errors.py new file mode 100644 index 0000000..3add76d --- /dev/null +++ b/ap/errors.py @@ -0,0 +1,17 @@ +from flask import jsonify, Response as FlaskResponse + + +def bad_req(msg): + """400 bad request status code""" + res = FlaskResponse() + res.status_code = 400 + res.data = msg + return res + + +def not_found(msg): + """404 not found request status code""" + res = FlaskResponse() + res.status_code = 404 + res.data = msg + return res diff --git a/ap/server.py b/ap/server.py new file mode 100644 index 0000000..ab9f707 --- /dev/null +++ b/ap/server.py @@ -0,0 +1,53 @@ +from flask import Flask, request, jsonify +from flask_cors import CORS, cross_origin + +from .config import Config +from .db import Database +from .errors import bad_req, not_found + +app = Flask(__name__) + + +JRD_JSON = "application/jrd+json; charset=utf-8" + +WEBFINGER_ROUTE = "/.well-known/webfinger" + +@app.route(WEBFINGER_ROUTE, methods=["GET"]) +@cross_origin() +def hello_world(): + config = Config() + db = Database() + + resource = request.args.get("resource") + if resource is None: + bad_req("webfinger must have 'resource' arg") + if "acct:" not in resource: + return bad_req("webfinger resource query must have 'acct'") + if "@" not in resource: + return bad_req("webfinger resource query must have '@'") + + parts = resource.split("acct:") + parts = parts[1].split("@") + username = parts[0] + hostname = parts[1] + if hostname != config.options["hostname"]: + bad_req(f"Only serves hostname {config.options['hostname']}") + user = db.get_user(username) + if user is None: + return not_found(f"resource {username} not found") + + resp = jsonify( + { + "subject": f"acct:{username}@{hostname}", + "aliases": [f"http://{hostname}/@{username}"], + "links": [ + { + "rel": "self", + "type": "application/activity+json", + "href": f"http://{hostname}/users/{username}", + }, + ], + } + ) + resp.headers["Content-Type"] = JRD_JSON + return resp