From 79e3bf726b7166c5ec641ea02b75f82a74f31698 Mon Sep 17 00:00:00 2001 From: pdp8 Date: Sun, 23 Apr 2023 19:26:00 +0200 Subject: inbox/outbox GET --- application.rb | 184 +++++++++++++++++--------------------------- config.ru | 1 + outbox/230401T23:00:01.json | 4 + outbox/230402T23:00:01.json | 10 +++ pdp8 | 42 ++++++++++ post.rb | 17 +++- webfinger | 10 +++ 7 files changed, 155 insertions(+), 113 deletions(-) create mode 100644 outbox/230401T23:00:01.json create mode 100644 outbox/230402T23:00:01.json create mode 100644 pdp8 create mode 100644 webfinger diff --git a/application.rb b/application.rb index 7618855..6e700fd 100644 --- a/application.rb +++ b/application.rb @@ -14,6 +14,9 @@ ACTOR = URI.join(SOCIAL_URL, USER) MATRIX = "@#{USER}:matrix.#{WWW_DOMAIN}" +OUTBOX = File.join(File.dirname(__FILE__), "outbox") +INBOX = File.join(File.dirname(__FILE__), "inbox") + class Application def call(env) code = 404 @@ -24,41 +27,21 @@ class Application when 'POST' case env["REQUEST_URI"] - when "/inbox" - type = "text/plain" - signature_header = {} - if env["HTTP_SIGNATURE"].split(',') - env["HTTP_SIGNATURE"].split(',').each do |pair| - k, v = pair.split('=') - signature_header[k] = v.gsub('"', '') - end - key_id = signature_header['keyId'] - headers = signature_header['headers'] - signature = Base64.urlsafe_decode64(signature_header['signature'].encode("ascii-8bit")) - uri = URI(key_id) - res = Net::HTTP.get_response(uri) - actor = JSON.parse(res.body) - key = OpenSSL::PKey::RSA.new(actor['publicKey']['publicKeyPem']) - - comparison_string = headers.split(' ').map do |signed_header_name| - if signed_header_name == '(request-target)' - '(request-target): post /inbox' - else - "#{signed_header_name}: #{env["HTTP_" + signed_header_name.upcase]}" - end - end.join("\n") - if key.verify(OpenSSL::Digest::SHA256.new, signature, comparison_string.encode("ascii-8bit")) - input = JSON.parse(env["rack.input"].gets) - # p input - code = 200 - response = "OK" - else - code = 401 - response = 'Request signature could not be verified' - end + when "/inbox" # receive from server + if verify(env) + input = JSON.parse(env["rack.input"].gets) + # p input["type"] + code = 200 + response = "OK" else code = 401 - response = 'Request signature could not be verified' + response = "not verified" + end + + when "/outbox" + input = JSON.parse(env["rack.input"].gets) + case input["type"] + when "Create" end end @@ -68,95 +51,72 @@ class Application case env["REQUEST_URI"] when "/.well-known/webfinger?resource=acct:#{ACCOUNT}" - code = 200 type = "application/jrd+json" - response = { - "subject" => "acct:#{ACCOUNT}", - "links" => [ - { - "rel" => "self", - "type" => "application/activity+json", - "href" => ACTOR - } - ] - } + response = File.read("webfinger") + code = 200 when "/#{USER}" + response = File.read("pdp8") code = 200 - response = { - "@context" => ["https://www.w3.org/ns/activitystreams"], - "id" => ACTOR, - "type" => "Person", - "preferredUsername" => USER, - "name" => USER, - "inbox" => URI.join(SOCIAL_URL, "inbox"), - "outbox" => URI.join(SOCIAL_URL, "outbox"), - "following" => URI.join(SOCIAL_URL, "following"), - "followers" => URI.join(SOCIAL_URL, "followers"), - "liked" => URI.join(SOCIAL_URL, "liked"), - "icon" => { - "type" => "Image", - "url" => "https://pdp8.info/pdp8.png" - }, - "attachment": [ - { - "type": "PropertyValue", - "name": "Web", - "value": "#{WWW_DOMAIN}" - }, - { - "type": "PropertyValue", - "name": "Fediverse", - "value": "@#{ACCOUNT}" - }, - { - "type": "PropertyValue", - "name": "Matrix", - "value": "#{MATRIX}" - } - ], - "publicKey" => { - "@context" => "https://w3id.org/security/v1", - "@type" => "Key", - "id" => "#{ACTOR}#main-key", - "owner" => ACTOR, - "publicKeyPem" => File.read("public.pem") - } - } when "/outbox" + response = ordered_collection OUTBOX code = 200 + + when "/inbox" type = "application/activity+json" - response = { - "@context" => "https://www.w3.org/ns/activitystreams", - "summary" => "", - "type" => "OrderedCollection", - "totalItems" => 2, - # TODO generate items from src/www - "orderedItems" => [ - { - "type" => "Note", - "name" => "A Simple Note", - "tag" => [ - { - "type" => "Hashtag", - "name" => "#activitypub", - "href" => "https://s3lph.me/activitypub/tags/activitypub" - }, - ] - }, - { - "type" => "Note", - "name" => "Another Simple Note" - } - ] - } - when "/following" - when "/followers" - when "/liked" + response = ordered_collection INBOX + code = 200 + + # when "/following" + # when "/followers" + # when "/liked" end end - [code, { "Content-Type" => type }, [response.to_json]] + [code, { "Content-Type" => type }, [response]] + end + + def ordered_collection dir + posts = Dir[File.join(dir, "*.json")].sort.reverse.collect { |f| p f; JSON.parse(File.read f) } + { + "@context" => "https://www.w3.org/ns/activitystreams", + "summary" => "pdp8 outbox", + "type" => "OrderedCollection", + "totalItems" => posts.size, + "orderedItems" => posts, + }.to_json + end + + def verify(env) + begin + signature_header = {} + env["HTTP_SIGNATURE"].split(',').each do |pair| + k, v = pair.split('=') + signature_header[k] = v.gsub('"', '') + end + key_id = signature_header['keyId'] + headers = signature_header['headers'] + signature = Base64.urlsafe_decode64(signature_header['signature'].encode("ascii-8bit")) + uri = URI(key_id) + res = Net::HTTP.get_response(uri) + actor = JSON.parse(res.body) + key = OpenSSL::PKey::RSA.new(actor['publicKey']['publicKeyPem']) + + comparison_string = headers.split(' ').map do |signed_header_name| + if signed_header_name == '(request-target)' + '(request-target): post /inbox' + else + "#{signed_header_name}: #{env["HTTP_" + signed_header_name.upcase]}" + end + end.join("\n") + if key.verify(OpenSSL::Digest::SHA256.new, signature, comparison_string.encode("ascii-8bit")) + true + else + false + end + rescue + false + end end end diff --git a/config.ru b/config.ru index 535b79a..22f309b 100644 --- a/config.ru +++ b/config.ru @@ -2,4 +2,5 @@ require_relative './application.rb' require 'rack/protection' use Rack::Protection, :except => :session_hijacking use Rack::Reloader +# use Verify run Application.new diff --git a/outbox/230401T23:00:01.json b/outbox/230401T23:00:01.json new file mode 100644 index 0000000..baeb7f8 --- /dev/null +++ b/outbox/230401T23:00:01.json @@ -0,0 +1,4 @@ +{ + "type": "Note", + "name": "A Simple Note" +} diff --git a/outbox/230402T23:00:01.json b/outbox/230402T23:00:01.json new file mode 100644 index 0000000..bd50adb --- /dev/null +++ b/outbox/230402T23:00:01.json @@ -0,0 +1,10 @@ +{ + "type": "Note", + "name": "Another Simple Note", + "tag": [ + { + "type": "Hashtag", + "name": "#test" + } + ] +} diff --git a/pdp8 b/pdp8 new file mode 100644 index 0000000..ceb25c9 --- /dev/null +++ b/pdp8 @@ -0,0 +1,42 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams" + ], + "id": "https://social.pdp8.info/pdp8", + "type": "Person", + "preferredUsername": "pdp8", + "name": "pdp8", + "inbox": "https://social.pdp8.info/inbox", + "outbox": "https://social.pdp8.info/outbox", + "following": "https://social.pdp8.info/following", + "followers": "https://social.pdp8.info/followers", + "liked": "https://social.pdp8.info/liked", + "icon": { + "type": "Image", + "url": "https://pdp8.info/pdp8.png" + }, + "attachment": [ + { + "type": "PropertyValue", + "name": "Web", + "value": "pdp8.info" + }, + { + "type": "PropertyValue", + "name": "Fediverse", + "value": "@pdp8@social.pdp8.info" + }, + { + "type": "PropertyValue", + "name": "Matrix", + "value": "@pdp8:matrix.pdp8.info" + } + ], + "publicKey": { + "@context": "https://w3id.org/security/v1", + "@type": "Key", + "id": "https://social.pdp8.info/pdp8#main-key", + "owner": "https://social.pdp8.info/pdp8", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4pZMYXoh8G+iEguDpKGD\n+eq+uDdhx/ch2x7rq00aPDDeHp40CG8bW1ZRC4WIOTUOgK4MeMDoaXT9/vWgr7xT\n/Qm95SEyZWBKqasBsp2uGkDxl23C6dB2eeshuAwt308Qzm2DeTrKPAw/XBAyWHDD\nfan2nWrtXcDJaeXhD/QE/w7Qiz5F2GCb/E/o46SwEyOJi13WxI9Jtuzh76xmwNsd\nwVWIBSu4zn0hg/wv+xtq/c/KLO4ZL54YiJXxRwrkDN7Xdnd18FwFuZ7fT8+kfiqF\nBnvle0OTKxumW46U7ivaylnqoSOvsYK6oyop/m2rl9Nh3sGdcmOsLoFVDg4gOjDf\niQIDAQAB\n-----END PUBLIC KEY-----\n" + } +} diff --git a/post.rb b/post.rb index ed7a964..c5e7cd7 100755 --- a/post.rb +++ b/post.rb @@ -6,7 +6,21 @@ require 'base64' require 'net/http' require 'uri' -document = { "a" => 2 } # .to_json +# document = { "a" => 2 } # .to_json +document = { + "@context": "https://www.w3.org/ns/activitystreams", + "type": "Like", + "actor": "https://example.net/~mallory", + "to": ["https://hatchat.example/sarah/", + "https://example.com/peeps/john/"], + "object": { + "@context": { "@language": "en" }, + "id": "https://example.org/~alice/note/23", + "type": "Note", + "attributedTo": "https://example.org/~alice", + "content": "I'm a goat" + } +} date = Time.now.utc.httpdate keypair = OpenSSL::PKey::RSA.new(File.read('private.pem')) signed_string = "(request-target): post /inbox\nhost: social.pdp8.info\ndate: #{date}" @@ -19,6 +33,7 @@ http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE header = { + 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', 'Host' => 'social.pdp8.info', 'Date' => date, 'Signature' => signed_header, diff --git a/webfinger b/webfinger new file mode 100644 index 0000000..507b34e --- /dev/null +++ b/webfinger @@ -0,0 +1,10 @@ +{ + "subject": "acct:pdp8@social.pdp8.info", + "links": [ + { + "rel": "self", + "type": "application/activity+json", + "href": "https://social.pdp8.info/pdp8" + } + ] +} -- cgit v1.2.3