# frozen_string_literal: true # server-server post '/inbox' do request.body.rewind # in case someone already read it @body = request.body.read halt 400 if @body.empty? begin @activity = JSON.parse @body rescue StandardError p @body halt 400 end type = @activity['type'].downcase.to_sym p type halt 501 unless respond_to?(type) @object = @activity['object'] @object = fetch(@object) if @object.is_a?(String) && @object.match(/^http/) halt 400 unless @object verify! unless type == :accept # pixelfed sends unsigned accept activities??? send(type) halt 200 end # public get '/.well-known/webfinger' do halt 404 unless request['resource'] == "acct:#{MENTION}" send_file(WEBFINGER, type: 'application/jrd+json') end ['/pdp8', '/following', '/followers', '/outbox', '/shared'].each do |path| get path do send_file(File.join(PUBLIC_DIR, path) + '.json', type: CONTENT_TYPE) end end helpers do def create return unless @object return if File.readlines(VISITED).collect { |l| l.chomp }.include? @object['id'] File.open(VISITED, 'a+') { |f| f.puts @object['id'] } update_collection INBOX, @object return unless @object['inReplyTo'] @object = fetch @object['inReplyTo'] create if @object end def announce create end def follow update_collection FOLLOWERS, @activity['actor'] outbox 'Accept', @activity, [@activity['actor']] end def accept return unless @object['type'] == 'Follow' update_collection FOLLOWING, @object['object'] end def undo return unless @object['type'] == 'Follow' update_collection FOLLOWERS, @object['actor'], true end # https://github.com/mastodon/mastodon/blob/main/app/controllers/concerns/signature_verification.rb def verify! # digest sha256 = OpenSSL::Digest.new('SHA256') digest = "SHA-256=#{sha256.base64digest(@body)}" halt 403 unless digest == request.env['HTTP_DIGEST'] # signature signature_params = {} request.env['HTTP_SIGNATURE'].split(',').each do |pair| k, v = pair.split('=') signature_params[k] = v.gsub('"', '') end key_id = signature_params['keyId'] headers = signature_params['headers'] signature = Base64.decode64(signature_params['signature']) actor = fetch key_id halt 403 unless actor key = OpenSSL::PKey::RSA.new(actor['publicKey']['publicKeyPem']) comparison = headers.split(' ').map do |signed_params_name| if signed_params_name == '(request-target)' '(request-target): post /inbox' elsif signed_params_name == 'content-type' "#{signed_params_name}: #{request.env['CONTENT_TYPE']}" else "#{signed_params_name}: #{request.env["HTTP_#{signed_params_name.upcase}"]}" end end.join("\n") halt 403 unless key.verify(OpenSSL::Digest.new('SHA256'), signature, comparison) end end