mb@mb
5 years ago
6 changed files with 140 additions and 2 deletions
@ -0,0 +1,2 @@ |
|||
*.pem |
|||
__pycache__ |
@ -1,3 +1,5 @@ |
|||
# ap.flask |
|||
# AP test in Flask |
|||
|
|||
This is a testing area! :) |
|||
|
|||
|
|||
an ActivityPub testing area in Flask |
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,101 @@ |
|||
#!/usr/bin/env python3 |
|||
|
|||
import flask, os, json |
|||
# import functions |
|||
|
|||
""" A basic ActivityPub server written in Flask, that listens to follow requests. |
|||
Based on this tutorial: https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/ """ |
|||
|
|||
""" |
|||
AP |
|||
ActivityPub |
|||
AlternativeProtocol |
|||
A...P... |
|||
|
|||
""" |
|||
|
|||
# Config |
|||
DOMAIN = 'https://ap.virtualprivateserver.space' |
|||
# domain = 'http://localhost:5000' # for local testing only, ActivityPub doesn't allow the usage of http:// (it only accepts https://) |
|||
def publicKey(): |
|||
if not os.path.exists('./public.pem'): |
|||
os.system('openssl genrsa -out private.pem 2048') |
|||
os.system('openssl rsa -in private.pem -outform PEM -pubout -out public.pem') |
|||
else: |
|||
publicKey = open('./public.pem', 'r').read() |
|||
PUBLICKEY = publicKey.replace('\n', '\\n') # JSON-LD doesn't want to work with linebreaks, |
|||
# but needs the \n character to know where to break the line ;) |
|||
return PUBLICKEY |
|||
INBOX = [] |
|||
|
|||
# Create the application. |
|||
APP = flask.Flask(__name__) |
|||
|
|||
@APP.route('/', methods=['GET']) |
|||
def index(): |
|||
""" Displays the index page accessible at '/' """ |
|||
|
|||
content = 'test - index ({})'.format(DOMAIN) |
|||
return content |
|||
|
|||
@APP.route('/.well-known/webfinger', methods=['GET']) |
|||
def webfinger(): |
|||
""" When you try to search for an account in eg. Mastodon, |
|||
this object is return, with a link pointing to a place |
|||
where more information about this user can be found. """ |
|||
|
|||
""" What is Webfinger? It is what allows us to ask a website, |
|||
“Do you have a user with this username?” and receive resource |
|||
links in response, with more information about this user. """ |
|||
|
|||
if flask.request.args.get('resource'): |
|||
query = flask.request.args.get('resource') # acct:alice@tada.club |
|||
actor = query.split(':')[1].split('@')[0] # alice |
|||
|
|||
json = flask.render_template('webfinger.json', query=query, actor=actor, domain=DOMAIN) |
|||
resp = flask.Response(json, status=200, mimetype='application/json') |
|||
return resp |
|||
|
|||
else: |
|||
return 'no query' |
|||
|
|||
@APP.route('/users/<actor>', methods=['GET']) |
|||
def return_actor(actor): |
|||
""" This returns the actor.json object when somebody in the |
|||
Fediverse searches for this user. It returns the paths of |
|||
this user's inbox, its preferred username and the user's public key. |
|||
And also a profile icon (image) now! :) """ |
|||
|
|||
preferredUsername = actor # but could become a custom username, set by the user, stored in the database |
|||
# this preferredUsername doesn't show up yet in Mastodon ... |
|||
|
|||
json = flask.render_template('actor.json', actor=actor, preferredUsername=preferredUsername, publicKey=publicKey(), domain=DOMAIN) # actor = alice |
|||
resp = flask.Response(json, status=200, mimetype='application/json') |
|||
return resp |
|||
|
|||
@APP.route('/inspect') |
|||
def inspect(): |
|||
return flask.Response(b'<br><br>'.join(INBOX), status=200) |
|||
|
|||
@APP.route('/users/<actor>/inbox', methods=['GET', 'POST']) |
|||
def inbox(actor): |
|||
""" To post to this inbox, you could use curl: |
|||
$ curl -d '{"key" : "value"}' -H "Content-Type: application/json" -X POST http://localhost:5001/users/test/inbox |
|||
|
|||
Or, with an actual Mastodon follow request: |
|||
$ curl -d '{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://post.lurk.org/02d04ed5-dda6-48f3-a551-2e9c554de745","type":"Follow","actor":"https://post.lurk.org/users/manetta","object":"https://ap.virtualprivateserver.space/users/test","signature":{"type":"RsaSignature2017","creator":"https://post.lurk.org/users/manetta#main-key","created":"2018-11-28T16:15:35Z","signatureValue":"XUdBg+Zj9pkdOXlAYHhOtZlmU1Jdt63zwh2cXoJ8E8C1C+KvgGilkyfPTud9VNymVwdUQRl+YEW9KAZiiGaHb9H+tdVUr9BEkuR5E/tGehbMZr1sakC+qPehe4s3bRKEpJjTTJnTiSHaW7V6Qvr1u6+MVts6oj32az/ixuB/CfodSr3K/K+jZmmOl6SIUqX7Xg7xGwOxIsYaR7g9wbcJ4qyzKcTPZonPMsONq9/RSm3SeQBo7WO1FKlQiFxVP/y5eFaFP8GYDLZyK7Nj5kDL5TannfEpuF8f3oyTBErQhcFQYKcBZNbuaqX/WiIaGjtHIL2ctJe0Psb5Nfshx4MXmQ=="}}' -H "Content-Type: application/json" -X POST http://localhost:5001/users/test/inbox |
|||
""" |
|||
|
|||
if flask.request.method == 'GET': |
|||
return '''This has been a <em>{}</em> request. <br> |
|||
It came with the following header: <br><br><em>{}</em><br><br> |
|||
You have searched for the actor <em>{}</em>. <br> |
|||
This is <em>{}</em>'s shared inbox: <br><br><em>{}</em>'''.format(flask.request.method, flask.request.headers, actor, DOMAIN, str(INBOX)) |
|||
|
|||
if flask.request.method == 'POST': |
|||
INBOX.append(flask.request.data) |
|||
return flask.Response(status=200) |
|||
|
|||
if __name__ == '__main__': |
|||
APP.debug=True |
|||
APP.run(port=5001) |
@ -0,0 +1,22 @@ |
|||
{ |
|||
"@context": [ |
|||
"https://www.w3.org/ns/activitystreams", |
|||
"https://w3id.org/security/v1" |
|||
], |
|||
|
|||
"id": "{{ domain }}/users/{{ actor }}", |
|||
"type": "Person", |
|||
"preferredUsername": "{{ preferredUsername }}", |
|||
"inbox": "{{ domain }}/users/{{ actor }}/inbox", |
|||
|
|||
"publicKey": { |
|||
"id": "{{ domain }}/users/{{ actor }}#main-key", |
|||
"owner": "{{ domain }}/users/{{ actor }}", |
|||
"publicKeyPem": "{{ publicKey }}" |
|||
}, |
|||
"icon": { |
|||
"type": "Image", |
|||
"mediaType": "image/jpg", |
|||
"url": "https://ap.virtualprivateserver.space/static/icons/icon.jpg" |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
{ |
|||
"subject": "{{ query }}", |
|||
|
|||
"links": [ |
|||
{ |
|||
"rel": "self", |
|||
"type": "application/activity+json", |
|||
"href": "{{ domain }}/users/{{ actor }}" |
|||
} |
|||
] |
|||
} |
Loading…
Reference in new issue