From f8afb7dec94bf9248b4032b7e95549361f190f3c Mon Sep 17 00:00:00 2001 From: pdp8 Date: Sun, 23 Apr 2023 02:37:14 +0200 Subject: initial import --- .gitignore | 1 + application.rb | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.ru | 6 +++ post.rb | 12 +++++ 4 files changed, 174 insertions(+) create mode 100644 .gitignore create mode 100644 application.rb create mode 100644 config.ru create mode 100644 post.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfaad76 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pem diff --git a/application.rb b/application.rb new file mode 100644 index 0000000..6abf490 --- /dev/null +++ b/application.rb @@ -0,0 +1,155 @@ +require 'json' +require 'net/http' +require 'uri' + +USER = "pdp8" +WWW_DOMAIN = "pdp8.info" +WWW_URL = "https://#{WWW_DOMAIN}" +SOCIAL_DOMAIN = "social.#{WWW_DOMAIN}" + +ACCOUNT = "#{USER}@#{SOCIAL_DOMAIN}" +SOCIAL_URL = "https://#{SOCIAL_DOMAIN}" +ACTOR = URI.join(SOCIAL_URL, USER) + +MATRIX = "@#{USER}:matrix.#{WWW_DOMAIN}" + +class Application + def call(env) + code = 404 + type = "application/activity+json" + + case env['REQUEST_METHOD'] + + when 'POST' + case env["REQUEST_URI"] + + when "/inbox" + type = "text/plain" + 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.decode64(signature_header['signature']) + 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) + input = JSON.parse(env["rack.input"].gets) + code = 200 + response = "OK" + else + code = 401 + response = 'Request signature could not be verified' + end + + end + + when 'GET' + + 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 + } + ] + } + + when "/#{USER}" + 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" + code = 200 + 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" + end + + end + [code, { "Content-Type" => type }, [response.to_json]] + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..e2aedfd --- /dev/null +++ b/config.ru @@ -0,0 +1,6 @@ +require_relative './application.rb' +require 'rack/protection' +use Rack::Protection, :except => :session_hijacking +use Rack::Reloader +# use Rack::ContentType, "application/ld+json" +run Application.new diff --git a/post.rb b/post.rb new file mode 100644 index 0000000..dc881b1 --- /dev/null +++ b/post.rb @@ -0,0 +1,12 @@ +require 'http' +require 'openssl' + +document = File.read('create-hello-world.json') +date = Time.now.utc.httpdate +keypair = OpenSSL::PKey::RSA.new(File.read('private.pem')) +signed_string = "(request-target): post /inbox\nhost: mastodon.social\ndate: #{date}" +signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string)) +header = 'keyId="https://my-example.com/actor",headers="(request-target) host date",signature="' + signature + '"' + +HTTP.headers({ 'Host': 'mastodon.social', 'Date': date, 'Signature': header }) + .post('https://mastodon.social/inbox', body: document) -- cgit v1.2.3