""" Perform WebFinger lookups according to RFC7033 """ import json from fastapi import APIRouter from fastapi.responses import PlainTextResponse, JSONResponse from fastapi.params import Query from pydantic import ValidationError from webfinger.io import get_jrd from webfinger.models import JRD, JRDResponse router = APIRouter( prefix="/.well-known/webfinger", ) # Define required CORS header per RFC7033 HEADERS = {'Access-Control-Allow-Origin': '*'} # if env.get("CORS_ALLOW_ORIGIN"): # allowed_origins = env.get("CORS_ALLOW_ORIGIN") # headers.update({'Access-Control-Allow-Origin': allowed_origins}) ## Example responses for OpenAPI schema RESOURCE_NOT_PROVIDED = \ "Query ?resource={resource} to obtain information about that resource." RESOURCE_NOT_FOUND = \ "Resource not found" RESOURCE_NOT_PARSED = \ "Resource contains invalid JSON or was unreadable" RESOURCE_NOT_VALID = \ "Some errors occurred while validating the JRD" RESPONSES = { 200: { "description": ( "OK:" " The resource you requested was found," " and has the returned resource document."), "model": JRD, "content": { "application/jrd+json": { "example": json.loads(JRD.Config.schema_extra['example']), }, }, }, 400: { "description": ( "Bad Request:" " No resource was provided."), "content": { "text/plain": { "example": RESOURCE_NOT_PROVIDED }, }, }, 404: { "description": ( "Not Found:" " Could not find a document for the provided resource."), "content": { "text/plain": { "example": RESOURCE_NOT_FOUND }, }, }, 500: { "description": ( "Server Error:" " The resource exists," " but contains invalid JSON or was unreadable."), "content": { "text/plain": { "example": RESOURCE_NOT_PARSED }, }, }, } ## The lookup method as described in RFC7033 @router.get( "", summary = "Lookup a Webfinger resource", description = ( "Query the Webfinger service for a resource URI" " and obtain its associated resource document"), tags = ["RFC7033"], response_model = JRD, response_class = JRDResponse, responses = {**RESPONSES}, ) async def lookup( resource: str = Query( None, title = "Resource URI", description = ( "The subject whose document you are looking up." " If not provided, you will get a 400 Bad Request." ), ), rel: list[str] = Query( None, title = "Link relation", description = ( "If provided, filter the links array for this rel-value only." " This parameter may be included multiple times." ), ) ): """ Respond to a WebFinger query. """ # If no resource is given, then show a basic hint. if not resource: return PlainTextResponse( content = RESOURCE_NOT_PROVIDED, status_code=400, headers = HEADERS, ) # Otherwise, try to read the resource document. try: jrd = get_jrd(resource) except FileNotFoundError: # JRD file does not exist return PlainTextResponse( content = RESOURCE_NOT_FOUND, status_code = 404, headers = HEADERS, ) except (OSError, json.JSONDecodeError): # JRD file could not be read or parsed return PlainTextResponse( content = RESOURCE_NOT_PARSED, status_code = 500, headers = HEADERS, ) except ValidationError as exception: # JRD was parsed but contained type or value errors errors = [RESOURCE_NOT_VALID] for error in exception.errors(): print(error) errors.append(f"{error['msg']} for {error['loc'][0]}") return PlainTextResponse( content = '\n- '.join(errors), status_code=500, headers = HEADERS, ) # If optional rel is provided, then filter only for links with that rel-type. if rel: jrd.links = [link for link in jrd.links if link.rel in rel] # Construct JSON response and return it. return JSONResponse( content = jrd.dict(exclude_none=True), media_type = "application/jrd+json", headers = HEADERS, )