From 2e614738f72c9af634c4a630693d06f144bc24df Mon Sep 17 00:00:00 2001 From: pdp8 Date: Wed, 12 Jul 2023 14:49:22 +0200 Subject: signed fetch for mastodon instances with AUTHORIZED_FETCH (e.g. mastodon.art) --- activitypub.rb | 4 ++-- client.rb | 6 +++--- helpers.rb | 38 ++++++++++++++++++++++++++++---------- public/pdp8 | 2 +- server.rb | 7 ++++++- 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/activitypub.rb b/activitypub.rb index 93bb45a..aef92a7 100644 --- a/activitypub.rb +++ b/activitypub.rb @@ -7,7 +7,6 @@ require 'sinatra' SOCIAL_DIR = '/srv/social/' INBOX_DIR = File.join(SOCIAL_DIR, 'inbox') -# ARCHIVE_DIR = File.join(SOCIAL_DIR, 'archive') PUBLIC_DIR = File.join(SOCIAL_DIR, 'public') OUTBOX_DIR = File.join(PUBLIC_DIR, 'outbox') FOLLOWERS = File.join(PUBLIC_DIR, 'followers') @@ -25,7 +24,8 @@ FOLLOWING_URL = File.join(SOCIAL_URL, 'following') enable :sessions set :session_secret, File.read('.secret').chomp -set :default_content_type, 'application/activity+json' +# set :default_content_type, 'application/activity+json' +set :default_content_type, 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' set :port, 9292 require_relative 'helpers' diff --git a/client.rb b/client.rb index 8f3557a..c2c64ea 100644 --- a/client.rb +++ b/client.rb @@ -3,7 +3,7 @@ # client-server post '/' do protected! - date = Time.now.strftime('%Y-%m-%dT%H:%M:%S.%N') + Time.now.strftime('%Y-%m-%dT%H:%M:%S.%N') recipients = public recipients << params[:to] @@ -70,7 +70,7 @@ end post '/follow' do protected! - actor, mention = parse_follow params['follow'] + actor, = parse_follow params['follow'] outbox 'Follow', actor, [actor] redirect(params['anchor'] || '/inbox') end @@ -109,7 +109,7 @@ end @threads << object else collection.select { |o| o['id'] == object['inReplyTo'] }.each do |o| - object['indent'] = o['indent'] + 4 + object['indent'] = o['indent'] + 2 o['replies'] << object end end diff --git a/helpers.rb b/helpers.rb index 556f187..212c406 100644 --- a/helpers.rb +++ b/helpers.rb @@ -6,11 +6,26 @@ helpers do def curl(ext, url) p url response = `/run/current-system/sw/bin/curl --fail-with-body -sSL #{ext} #{url}` - $CHILD_STATUS.success? ? response : nil + if $CHILD_STATUS.success? + response + else + p response + nil + end end def fetch(url, accept = 'application/activity+json') - response = curl("-H 'Accept: #{accept}'", url) + uri = URI(url) + httpdate = Time.now.utc.httpdate + keypair = OpenSSL::PKey::RSA.new(File.read('private.pem')) + string = "(request-target): get #{uri.request_uri}\nhost: #{uri.host}\ndate: #{httpdate}" + signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), string)) + signed_header = "keyId=\"#{ACTOR}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date\",signature=\"#{signature}\"" + + response = curl( + "-H 'Accept: #{accept}' -H 'Host: #{uri.host}' -H 'Date: #{httpdate}' -H 'Signature: #{signed_header}' ", url + ) + # response = curl("-H 'Accept: #{accept}'", url) response ? JSON.parse(response) : nil end @@ -30,7 +45,7 @@ helpers do basename = "#{date}.json" activity['id'] = File.join(OUTBOX_URL, basename) activity['published'] = httpdate - if activity['object'] and activity['object']['type'] and !activity['object']['id'] + if activity['object'] && activity['object']['type'] && !activity['object']['id'] rel_path = File.join activity['object']['type'].downcase, basename activity['object']['published'] = httpdate activity['object']['id'] = File.join(OUTBOX_URL, rel_path) @@ -46,16 +61,19 @@ helpers do # assumes that recipient collections have been expanded by sender # put all recipients into 'to', avoid 'cc' 'bto' 'bcc' 'audience' !! activity['to'] = recipients if add_recipients - inboxes = if recipients.include? 'https://www.w3.org/ns/activitystreams#Public' - people.collect { |p| p[2] }.uniq # cached sharedInboxes - else - [] - end + inboxes = [] + # inboxes = if recipients.include? 'https://www.w3.org/ns/activitystreams#Public' + # people.collect { |p| p[2] }.uniq # cached sharedInboxes + # else + # [] + # end recipients.uniq.each do |url| next if [ACTOR, 'https://www.w3.org/ns/activitystreams#Public'].include? url + p 'FETCH', url actor = fetch url - next unless actor and actor['inbox'] + p actor + next unless actor && actor['inbox'] inbox = actor['endpoints']['sharedInbox'] inboxes << (inbox || actor['inbox']) @@ -109,7 +127,7 @@ helpers do end def cache(mention, actor, a) - sharedInbox = a['endpoints']['sharedInbox'] if a['endpoints'] and a['endpoints']['sharedInbox'] + sharedInbox = a['endpoints']['sharedInbox'] if a['endpoints'] && a['endpoints']['sharedInbox'] File.open('cache/people.tsv', 'a') { |f| f.puts "#{mention}\t#{actor}\t#{sharedInbox}" } end end diff --git a/public/pdp8 b/public/pdp8 index 36c4ac9..c066ea6 100644 --- a/public/pdp8 +++ b/public/pdp8 @@ -34,7 +34,7 @@ ], "endpoints": { "sharedInbox": "https://social.pdp8.info/inbox" - } + }, "publicKey": { "id": "https://social.pdp8.info/pdp8#main-key", "owner": "https://social.pdp8.info/pdp8", diff --git a/server.rb b/server.rb index 11b6411..3263d9d 100644 --- a/server.rb +++ b/server.rb @@ -14,10 +14,11 @@ post '/inbox' do type = @activity['type'].downcase.to_sym p type halt 501 unless respond_to?(type) + # jj @activity @object = @activity['object'] @object = fetch(@object) if @object.is_a?(String) && @object.match(/^http/) halt 400 unless @object - verify! + # verify! unless type == :accept # pixelfed sends unsigned accept activities send(type) end @@ -41,6 +42,10 @@ end end end +get '/pdp8' do + send_file('./public/pdp8') +end + get '/tags/:tag' do |tag| ordered_collection(File.join(TAGS, tag)).to_json end -- cgit v1.2.3