# This is a simple python flask implementation of 'How To Implement A Basic ActivityPub Server' # https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/ # © 2018 homebrewserver.club contributors # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import flask import os import requests import base64 import json from flask import request from flask import Response from time import strftime, gmtime from Crypto.Hash import SHA256 from Crypto.Signature import pkcs1_15 from Crypto.PublicKey import RSA from httpsig import HTTPSigAuth from key import Key, get_key # import logging import http.client as http_client http_client.HTTPConnection.debuglevel = 1 logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) requests_log.propagate = True # #Config DOMAIN = 'https://my-example.com' USERNAME = 'alice' key = get_key(f'{DOMAIN}/users/{USERNAME}') #generate keypair on first launch #Flask app = flask.Flask(__name__) @app.route('/') def index(): return 'It works! Now try to look for a user@my-example.com via the mastodon interface' @app.route('/.well-known/webfinger') def finger(): """ Respond to webfinger queries (GET /.well-known/webfinger?resource=acct:alice@my-example.com) with a json object pointing to the actor see templates/webfinger.json """ if request.args.get('resource'): query = request.args.get('resource') actor = query.split(':')[1].split('@')[0] # from 'acct:alice@my-example.com' to 'alice' json = flask.render_template('webfinger.json', query=query, actor=actor, domain=DOMAIN) # render our ActivityPub answer return Response(response=json, status=200, mimetype="application/json") # return that answer as a json object @app.route('/users/') def profile(actor): """ Return an Actor object see templates/actor.json """ key = get_key(f'{DOMAIN}/users/{actor}') publicKeyPem = key.to_dict()['publicKeyPem'] publicKeyPem = publicKeyPem.replace('\n','\\n') #public key shouldn't contain verbatim linebreaks in json json = flask.render_template('actor.json', preferred_username=USERNAME, actor=actor, domain=DOMAIN, public_key=publicKeyPem) # render our ActivityPub answer return Response(response=json, status=200, mimetype="application/json") # return that answer as a json object @app.route('/post/', methods=['POST','GET']) def post(): #key = get_key(f'{DOMAIN}/users/{USERNAME}') headers = {"content-type": "application/activity+json", "user-agent": f"Basic AP v: 0.0"} activity=json.loads(flask.render_template('create.json', domain=DOMAIN, actor=USERNAME)) http_sig = HTTPSigAuth(key, headers) url = 'https://post.lurk.org/inbox' body = json.dumps(activity) headers = http_sig.sign(url,body) r = requests.post(url, json=body, headers=headers) html = f'Status code
{r.status_code}

Response Headers:
{r.headers}


json message:
{r.request.body}


Request headers:
{r.request.headers}
' html_headers = f'{html}' return html_headers if __name__ == '__main__': app.debug =True app.run()