[doc] show done?
All checks were successful
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
SouthFox 2023-07-14 02:04:41 +08:00
parent a49cec70b2
commit 33f9218a8d

View file

@ -88,7 +88,7 @@ ME = {
"sharedInbox": config.BASE_URL + "/inbox", "sharedInbox": config.BASE_URL + "/inbox",
}, },
"url": config.ID + "/", # Important for Mastodon "url": config.ID + "/", # Important for Mastodon
"manuallyApprovesFollowers": False, "manuallyApprovesFollowers": config.MANUALLY_APPROVES_FOLLOWERS,
"attachment": [], "attachment": [],
"icon": { "icon": {
"mediaType": "image/png", "mediaType": "image/png",
@ -118,7 +118,6 @@ def locate_user() -> Response:
GET http://coscup.localhost/user/show GET http://coscup.localhost/user/show
#+end_src #+end_src
** WebFinger ** WebFinger
#+begin_src python #+begin_src python
from flask import Flask, abort, request, Response, jsonify from flask import Flask, abort, request, Response, jsonify
@ -163,9 +162,7 @@ def wellknown_webfinger() -> Response:
GET http://coscup.localhost/.well-known/webfinger?resource=acct:show@coscup.localhost GET http://coscup.localhost/.well-known/webfinger?resource=acct:show@coscup.localhost
#+end_src #+end_src
** HTTP Signature ** HTTP Signature
other.localhost --Follow-> coscup.localhost other.localhost --Follow-> coscup.localhost
#+begin_src json #+begin_src json
@ -174,7 +171,7 @@ other.localhost --Follow-> coscup.localhost
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": "http://other.localhost/9ef8847d72434cff82254f36ff88e710", "id": "http://other.localhost/9ef8847d72434cff82254f36ff88e710",
"type": "Follow", "type": "Follow",
"actor": "http://other.localhost", "actor": "http://other.localhost/accounts/other",
"object": "http://coscup.localhost/meow/show" "object": "http://coscup.localhost/meow/show"
} }
@ -205,15 +202,14 @@ def parse_signature(
detail[name.lower()] = value detail[name.lower()] = value
signature_details = { signature_details = {
"headers": detail["headers"].split(), "headers": detail["headers"].split(),
"signature": (detail["signature"]), "signature": detail["signature"],
"algorithm": detail["algorithm"], "algorithm": detail["algorithm"],
"keyid": detail["keyid"], "keyid": detail["keyid"],
} }
return signature_details return signature_details
#+end_src #+end_src
parsed_signature = parse_signature(headers["signature"])
parse_signature(headers["signature"])
#+begin_src json #+begin_src json
{"algorithm": "rsa-sha256", {"algorithm": "rsa-sha256",
"headers": ["(request-target)", "user-agent", "host", "date", "digest", "content-type"], "headers": ["(request-target)", "user-agent", "host", "date", "digest", "content-type"],
@ -221,7 +217,6 @@ parse_signature(headers["signature"])
"signature": "irioDQuhYstSvafpl6DW4d31wDPRiTv7MZGyBo3j4kkc2TrfOweH3WRMMnoaWwl4LAI2WYLoKefeQpOg7Rm7ZEffsoLOzZvgdWJBm8lnOEgieyy5l2Vq1mlcS2PRJCisYGdzAwFOBkcHk0WKAZXvs1ieRV34NHfM8JF+DjrCBTZ/U9LyxULBwC6tPQTh9tflCOwXZOzXUq17C+2Uzsr8h4tDHjbmrG7OAcvYiPeOUKaP+InoE6j9ViHllhidNCPL0y8b1c7c72ruN48kF42OfyfUeiuCcuLwdp8eYBlTdG/ZsT2YXyKruwim3tTD1TtyW4Vfll+F/4/1RfWHsc9LrQ=="} "signature": "irioDQuhYstSvafpl6DW4d31wDPRiTv7MZGyBo3j4kkc2TrfOweH3WRMMnoaWwl4LAI2WYLoKefeQpOg7Rm7ZEffsoLOzZvgdWJBm8lnOEgieyy5l2Vq1mlcS2PRJCisYGdzAwFOBkcHk0WKAZXvs1ieRV34NHfM8JF+DjrCBTZ/U9LyxULBwC6tPQTh9tflCOwXZOzXUq17C+2Uzsr8h4tDHjbmrG7OAcvYiPeOUKaP+InoE6j9ViHllhidNCPL0y8b1c7c72ruN48kF42OfyfUeiuCcuLwdp8eYBlTdG/ZsT2YXyKruwim3tTD1TtyW4Vfll+F/4/1RfWHsc9LrQ=="}
#+end_src #+end_src
#+begin_src python #+begin_src python
import base64 import base64
from Crypto.Hash import SHA256 from Crypto.Hash import SHA256
@ -254,16 +249,17 @@ def build_signature_string(
return "\n".join(sign_str) return "\n".join(sign_str)
build_signature_string( signature_string = build_signature_string(
request.method # "post" request.method # "post"
request.path # "/inbox" request.path # "/inbox"
parse_signature(headers["signature"])["headers"], # ["(request-target)", "user-agent", "host", "date", "digest", "content-type"] parsed_signature["headers"], # ["(request-target)", "user-agent", "host", "date", "digest", "content-type"]
calculate_digest(request.data), calculate_digest(request.data),
headers, headers,
) )
#+end_src #+end_src
signature_string
#+begin_quote #+begin_quote
(request-target): post /inbox (request-target): post /inbox
user-agent: something/fediverse+app user-agent: something/fediverse+app
@ -272,3 +268,170 @@ date: Tue, 11 Jul 2023 04:21:11 GMT
digest: SHA-256=WzNniTyRBcZpgK7a6zYJBgsdKIQmFEPPfysh/ZzKb2o= digest: SHA-256=WzNniTyRBcZpgK7a6zYJBgsdKIQmFEPPfysh/ZzKb2o=
content-type: application/activity+json content-type: application/activity+json
#+end_quote #+end_quote
#+begin_src python
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
def verify_signature(
signature_string: str,
signature: bytes, # base64.b64decode(parsed_signature["signature"])
pubkey: str,
) -> bool :
pubkey = RSA.importKey(pubkey)
signer = PKCS1_v1_5.new(pubkey)
digest = SHA256.new()
digest.update(signature_string.encode("utf-8"))
return signer.verify(digest, signature)
#+end_src
** Activity!
*** Follow / Accept
other.localhost --Follow-> coscup.localhost
#+begin_src json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "http://other.localhost/9ef8847d72434cff82254f36ff88e710",
"type": "Follow",
"actor": "http://other.localhost/accounts/other",
"object": "http://coscup.localhost/meow/show"
}
#+end_src
#+begin_src python
def _process_inbox_request(...):
...
actor = feact_actor(ap_object["actor"])
...
if "Follow" == ap_object["type"]:
if _handle_follow(actor, ap_object):
...
elif "Create" == ap_object["type"]:
if _handle_create(ap_object):
...
def _handle_follow(...):
if ME["id"] != ap_object["object"]:
logger.warning("wrong follow object!" + .ap_object["id"])
return False
if MANUALLY_APPROVES_FOLLOWERS:
save_follow_request(ap_object)
return True
_send_accept(actor, ap_object)
save_follower()
return True
async def _send_accept(...) -> None :
reply_id = BASE_URL + str(uuid.uuid4())
actor_url = actor["inbox"]
payload = {
"@context": AS_CTX,
"id": reply_id,
"type": "Accept",
"actor": ID, # http://coscup.localhost/user/show
"object": ap_object["id"], # http://other.localhost/9ef8847d72434cff82254f36ff88e710
}
post(actor_url, payload)
#+end_src
*** Creat!
#+begin_src json
{
"@context" : [
"https://www.w3.org/ns/activitystreams",
{
"Emoji" : "toot:Emoji",
"atomUri" : "ostatus:atomUri",
"conversation" : "ostatus:conversation",
"focalPoint" : {
"@container" : "@list",
"@id" : "toot:focalPoint"
},
"inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
"ostatus" : "http://ostatus.org#",
"sensitive" : "as:sensitive",
"toot" : "http://joinmastodon.org/ns#",
"votersCount" : "toot:votersCount"
}
],
"type":"Create",
"published" : "2023-07-13T16:04:57Z",
"cc" : ["http://coscup.localhost/users/show/followers"],
"to" : ["https://www.w3.org/ns/activitystreams#Public"],
"id" : "http://coscup.localhost/79d42b46-aa2f-4837-835a-5f75ce9a253f",
"object": {
"attributedTo" : "http://coscup.localhost/user/show",
"cc" : ["http://coscup.localhost/users/show/followers"],
"to" : ["https://www.w3.org/ns/activitystreams#Public"],
"id" : "http://coscup.localhost/79d42b46-aa2f-4837-835a-5f75ce9a253f",
"content" : "<p>Hello World!</p>",
"inReplyTo" : null,
"published" : "2023-07-13T16:04:57Z",
"sensitive" : false,
"summary" : null,
"tag" : [],
"type" : "Note",
"url" : "http://coscup.localhost/79d42b46-aa2f-4837-835a-5f75ce9a253f"
},
}
#+end_src
#+begin_src python
def _send_create(...):
object_id = BASE_URL + str(uuid.uuid4())
to = []
cc = []
if visibility == "public":
to = [ap.AS_PUBLIC]
cc = [f"{BASE_URL}/followers"]
else:
raise ValueError(f"Unsupport visibility {visibility}")
ap_object = {
"type": "Note",
"id": object_id,
"attributedTo": ID,
"content": content,
"to": to,
"cc": cc,
"published": published,
"url": object_id,
"tag": [],
"content": content, # <p>Hello World!<p>
"summary": summary,
"inReplyTo": reply_id,
"sensitive": sensitive,
"attachment": [],
}
recipients = set()
for actor in get_followers():
recipients.add(actor.sharedInbox or actor.inbox_url)
ap_object = wrap_ap_object(ap_object)
for inbox_url in recipients:
post(inbox_url, ap_object,)
return True
def wrap_ap_object(...):
return {
"@context": AS_EXTENDED_CTX,
"actor": config.ID,
"to": ap_object.get("to", []),
"cc": ap_object.get("cc", []),
"id": ap_object["id"],
"object": ap_object,
"published": now()
"type": "Create",
}
#+end_src