summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpdp8 <pdp8@pdp8.info>2023-05-29 18:53:19 +0200
committerpdp8 <pdp8@pdp8.info>2023-05-29 18:53:19 +0200
commit5f309bae1ec181c20474f6bb3d18f86023dfb3df (patch)
treec242c57c0743cd459882e64a2c3c0397661a343d
parent451757d05a6464194a741c54e879b338d6329bd6 (diff)
Create, Undo, Delete, Update, Follow implemented
-rw-r--r--activitypub.rb232
1 files changed, 77 insertions, 155 deletions
diff --git a/activitypub.rb b/activitypub.rb
index 400465a..c5f5079 100644
--- a/activitypub.rb
+++ b/activitypub.rb
@@ -7,13 +7,13 @@
# test with pleroma etc
=begin
require 'json'
-require 'uri'
-require 'base64'
require 'securerandom'
require 'fileutils'
-require 'digest/sha2'
require 'nokogiri'
=end
+require 'uri'
+require 'base64'
+require 'digest/sha2'
require 'net/http'
require 'sinatra'
@@ -33,12 +33,34 @@ set :port, 9292
post "/inbox" do
request.body.rewind # in case someone already read it
body = request.body.read
- object = JSON.parse body
- case object["type"]
+ action = JSON.parse body
+
+ case action["type"]
when "Create"
- File.open(File.join("inbox", "#{object["published"]}-#{mention object["actor"]}.json"), "w+") { |f|
- f.puts object["object"].to_json
- }
+ create action["object"]
+ when "Delete"
+ delete action["object"]
+ when "Update"
+ delete action["object"]
+ create action["object"]
+ when "Follow"
+ File.open(File.join("public", "followers", mention(action["actor"]) + ".json"), "w+") { |f| f.puts body }
+ accept = { "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => File.join(SOCIAL_URL + "#accepts", SecureRandom.uuid),
+ "type" => "Accept",
+ "actor" => ACTOR,
+ "object" => object }
+ send_signed accept, accept["object"]["actor"]
+ when "Undo"
+ o = action["object"]
+ case o["type"]
+ when "Follow"
+ Dir["public/followers/*.json"].each do |follower|
+ FileUtils.rm follower if JSON.parse(File.read(follower))["actor"] == o["actor"]
+ end
+ end
+ else
+ p body
end
end
@@ -51,12 +73,12 @@ get "/.well-known/webfinger" do
end
get "/inbox", :provides => 'html' do
- template = "<!DOCTYPE html>
+ erb "<!DOCTYPE html>
<html lang='en'>
<body>
- <% Dir['./inbox/*'].sort.each do |file| %>
+ <% Dir['./inbox/*'].sort.each_with_index do |file,i| %>
<% item = JSON.parse(File.read(file)) %>
- <b><%= mention item['actor'] %></b>&nbsp;<i><%= item['published'].sub('T', ' ') %></i>
+ <%= i+1 %>&nbsp;<b><%= mention item['attributedTo'] %></b>&nbsp;<i><%= item['published'].sub('T', ' ') %></i>
<p><%= item['content'] %>
<% if item['attachment']
item['attachment'].each do |att|
@@ -90,11 +112,17 @@ get "/inbox", :provides => 'html' do
<% end %>
</body>
</html>"
- erb template
end
-get "/inbox", :provides => 'json' do
- ordered_collection("inbox").to_json
+def delete object
+ Dir["inbox/*.json"].each do |doc|
+ FileUtils.rm doc if JSON.parse(File.read(doc))["id"] == object["id"]
+ end
+end
+
+def create object
+ doc = File.join("inbox", "#{object["published"]}-#{mention object["attributedTo"]}.json")
+ File.open(doc, "w+") { |f| f.puts object.to_json }
end
def mention actor
@@ -121,6 +149,41 @@ def ordered_collection dir
"orderedItems" => posts,
}
end
+
+def send_signed object, url
+ # https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb
+ keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
+ date = Time.now.utc.httpdate
+ uri = URI.parse(url)
+
+ sha256 = OpenSSL::Digest::SHA256.new
+ body = object.to_json
+ digest = "SHA-256=" + sha256.base64digest(body)
+
+ signed_string = "(request-target): post #{inbox uri}\nhost: #{uri.host}\ndate: #{date}\ndigest: #{digest}\ncontent-type: application/activity+json"
+ signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
+ signed_header = 'keyId="' + ACTOR + '#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="' + signature + '"'
+
+ uri = URI.parse(get(url)["inbox"])
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
+ header = {
+ 'Content-Type' => 'application/activity+json',
+ 'Host' => uri.host,
+ 'Date' => date,
+ 'Digest' => digest,
+ 'Signature' => signed_header,
+ }
+ request = Net::HTTP::Post.new(uri.request_uri, header)
+ request.body = body
+
+ http.request(request)
+end
+
+def inbox uri
+ URI(get(uri)["inbox"]).request_uri
+end
+
=begin
post "/outbox" do
@@ -138,47 +201,6 @@ class Application
input = env["rack.input"].read
case env["REQUEST_PATH"]
- when "/inbox" # receive from server
- if verify(env)
- begin
- object = JSON.parse(input)
- case object["type"]
- when "Delete"
- puts input
- when "Follow"
- File.open(File.join("followers", SecureRandom.uuid + ".json"), "w+") { |f| f.puts input }
- accept = { "@context" => "https://www.w3.org/ns/activitystreams",
- "id" => File.join(SOCIAL_URL + "#accepts", SecureRandom.uuid),
- "type" => "Accept",
- "actor" => ACTOR,
- "object" => JSON.parse(input) }
- send accept, [accept["object"]["actor"]]
- when "Undo"
- o = object["object"]
- case o["type"]
- when "Follow"
- Dir["followers/*.json"].each do |follower|
- if JSON.parse(File.read(follower))["actor"] == o["actor"]
- FileUtils.rm follower
- end
- end
- else
- puts input
- end
- else
- puts input
- end
- code = 200
- response = "OK"
- rescue => e
- puts input, e.to_s
- response = "Request body contains invalid json."
- end
- else
- code = 403
- response = "Key verification failed for POST to #{env["REQUEST_URI"]}."
- end
-
when %r{/delete} # receive from client
if auth(env)
FileUtils.rm env["REQUEST_URI"].sub("/delete/", "")
@@ -235,67 +257,6 @@ class Application
end
end
- when 'GET'
-
- case env["REQUEST_URI"] # REQUEST_PATH does not contain queries
-
- when "/inbox"
- if auth(env)
- case env["HTTP_ACCEPT"]
- else
- type = "text/html"
- response = html env["REQUEST_PATH"]
- end
- code = 200
- else
- code = 403
- response = "You are not allowed to GET #{env["REQUEST_URI"]}."
- end
-
- when %r{/[outbox|following|followers|likes|shares]}
- response = ordered_collection(env["REQUEST_PATH"]).to_json
- code = 200
- end
-
- end
- [code, { "Content-Type" => type }, [response]]
- end
-
- def html path
-
- end
-=end
-
-=begin
- def html o
- html = "<!DOCTYPE html>
-<html lang='en'>
- <body>
- <b>#{mention o["actor"]}</b>&nbsp;<i>#{o["object"]["published"]}</i>
- <p>#{o["object"]["content"]}
- "
- if o["object"]["attachment"]
- o["object"]["attachment"].each do |att|
- case att["mediaType"]
- when /audio/
- html<< "\n<br><audio controls=''><source src='#{att["url"]}' type='#{att["mediaType"]}'></audio>"
- when /image/
- html << "\n<br><a href='#{att["url"]}'><img src='#{att["url"]}'></a>"
- when /video/
- html<< "\n<br><video controls=''><source src='#{att["url"]}' type='#{att["mediaType"]}'></video>"
- else
- html<< att + "<br>"
- html << "\n<a href='#{att["url"]}'>#{att["url"]}</a>"
- end
- end
- end
- end
- html << "\n\t</body>\n</html>"
- html
- end
-=end
-
-=begin
def parse input
date = Time.now.strftime("%Y-%m-%dT%H:%M:%S")
# TODO media attachments, hashtags
@@ -355,45 +316,6 @@ class Application
}[0]["href"]
end
- def send object, urls
- # https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb
- keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
- responses = []
- urls.each do |url|
- date = Time.now.utc.httpdate
- uri = URI.parse(url)
-
- sha256 = OpenSSL::Digest::SHA256.new
- body = object.to_json
- digest = "SHA-256=" + sha256.base64digest(body)
-
- signed_string = "(request-target): post #{inbox uri}\nhost: #{uri.host}\ndate: #{date}\ndigest: #{digest}\ncontent-type: application/activity+json"
- signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
- signed_header = 'keyId="' + ACTOR + '#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="' + signature + '"'
-
- uri = URI.parse(get(url)["inbox"])
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true
- header = {
- 'Content-Type' => 'application/activity+json',
- 'Host' => uri.host,
- 'Date' => date,
- 'Digest' => digest,
- 'Signature' => signed_header,
- }
- request = Net::HTTP::Post.new(uri.request_uri, header)
- request.body = body
-
- responses << http.request(request)
- end
- # puts responses
- responses
- end
-
- def inbox uri
- URI(get(uri)["inbox"]).request_uri
- end
-
def path object
object["id"].sub(SOCIAL_URL, '').sub('/', '')
end