summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.rb18
-rw-r--r--helpers.rb20
-rw-r--r--server.rb57
3 files changed, 54 insertions, 41 deletions
diff --git a/client.rb b/client.rb
index 2e449bd..3b30861 100644
--- a/client.rb
+++ b/client.rb
@@ -66,7 +66,8 @@ post "/outbox" do
File.open(outbox_path, "w+") { |f| f.puts create.to_json }
File.open(notes_path, "w+") { |f| f.puts create["object"].to_json }
- create["to"].each { |r| send_signed create, r }
+ recipients.delete "https://www.w3.org/ns/activitystreams#Public"
+ recipients.each { |r| send_signed create, r }
redirect to(params['redirect'])
end
@@ -92,6 +93,7 @@ post "/follow/*" do
protected!
mention = params['splat'][0]
actor = actor(mention)
+ return 502 unless actor
follow = { "@context" => "https://www.w3.org/ns/activitystreams",
"id" => File.join(SOCIAL_URL, "following", mention + ".json"),
"type" => "Follow",
@@ -105,6 +107,7 @@ post "/unfollow/*" do
protected!
mention = params['splat'][0]
actor = actor(mention)
+ return 502 unless actor
following_path = File.join("public", "following", mention + ".json")
if File.exists?(following_path)
undo = { "@context" => "https://www.w3.org/ns/activitystreams",
@@ -144,12 +147,12 @@ end
helpers do
def protected!
- redirect("/login.html") unless session['client']
+ halt 403 unless session['client']
end
def items
nr = 0
- files = Dir[File.join(@dir, '*.json')] + Dir['public/objects/*.json']
+ files = Dir[File.join(@dir, '*.json')] + Dir['public/notes/*.json']
@items = files.sort.collect do |file|
item = JSON.parse(File.read(file))
mention = mention(item['attributedTo'])
@@ -199,7 +202,9 @@ helpers do
def mention actor
person = people.select{|p| p[1] == actor}
if person.empty?
- mention = "#{fetch(actor)["preferredUsername"]}@#{URI(actor).host}"
+ a = fetch(actor)
+ return nil unless a
+ mention = "#{a["preferredUsername"]}@#{URI(actor).host}"
File.open('cache/people.tsv','a'){|f| f.puts "#{mention}\t#{actor}"}
mention
else
@@ -212,8 +217,9 @@ helpers do
actors = people.select{|p| p[0] == mention}
if actors.empty?
user, server = mention.split("@")
- actor = fetch("https://#{server}/.well-known/webfinger?resource=acct:#{mention}",
- "application/jrd+json")["links"].select { |l|
+ a = fetch("https://#{server}/.well-known/webfinger?resource=acct:#{mention}", "application/jrd+json")
+ return nil unless a
+ actor = a["links"].select { |l|
l["rel"] == "self"
}[0]["href"]
File.open('cache/people.tsv','a'){|f| f.puts "#{mention}\t#{actor}"}
diff --git a/helpers.rb b/helpers.rb
index 66dca3d..dca18e7 100644
--- a/helpers.rb
+++ b/helpers.rb
@@ -1,10 +1,13 @@
helpers do
+ def curl ext, url
+ response = `/run/current-system/sw/bin/curl -ifsSL #{ext} #{url}`
+ $?.success? ? response : nil
+ end
+
def fetch url, accept = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
- p url
- response = `/run/current-system/sw/bin/curl --fail-with-body -sSL -H 'Accept: #{accept}' #{url}`
- halt 400 unless $?.success?
- JSON.parse(response)
+ response = curl("-H 'Accept: #{accept}'", url)
+ response ? JSON.parse(response) : nil
end
# https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb
@@ -12,20 +15,21 @@ helpers do
keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
date = Time.now.utc.httpdate
- sha256 = OpenSSL::Digest::SHA256.new
body = object.to_json
+ sha256 = OpenSSL::Digest::SHA256.new
digest = "SHA-256=" + sha256.base64digest(body)
host = URI.parse(url).host
inbox = fetch(url)["inbox"]
+ return false unless inbox
request_uri = URI(inbox).request_uri
signed_string = "(request-target): post #{request_uri}\nhost: #{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 + '"'
- #p url
- puts `/run/current-system/sw/bin/curl --fail-with-body -sSL -X POST -H 'Content-Type: application/activity+json' -H 'Host: #{host}' -H 'Date: #{date}' -H 'Digest: #{digest}' -H 'Signature: #{signed_header}' -d '#{body}' #{inbox}`
- #puts `/run/current-system/sw/bin/curl -iL -X POST -H 'Content-Type: application/activity+json' -H 'Host: #{host}' -H 'Date: #{date}' -H 'Digest: #{digest}' -H 'Signature: #{signed_header}' -d '#{body}' #{inbox}`
+ curl "-X POST -H 'Content-Type: application/activity+json' -H 'Host: #{host}' -H 'Date: #{date}' -H 'Digest: #{digest}' -H 'Signature: #{signed_header}' -d '#{body}'", inbox
+ $?.success?
+
end
end
diff --git a/server.rb b/server.rb
index b5076f3..75c8ede 100644
--- a/server.rb
+++ b/server.rb
@@ -79,36 +79,39 @@ helpers do
# https://github.com/mastodon/mastodon/blob/main/app/controllers/concerns/signature_verification.rb
def verify!
- # TODO verify digest
- begin
- 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
- key = OpenSSL::PKey::RSA.new(actor['publicKey']['publicKeyPem'])
+ # verify digest
+ request.body.rewind # in case someone already read it
+ body = request.body.read
+ sha256 = OpenSSL::Digest::SHA256.new
+ digest = "SHA-256=" + sha256.base64digest(body)
+ halt 403 unless digest == request.env["HTTP_DIGEST"]
+
+ signature_params = {}
+ request.env["HTTP_SIGNATURE"].split(',').each do |pair|
+ k, v = pair.split('=')
+ signature_params[k] = v.gsub('"', '')
+ end
- 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")
+ 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 400 unless key.verify(OpenSSL::Digest.new('SHA256'), signature, comparison)
- rescue => e
- p request.env["HTTP_SIGNATURE"], e
- halt 400
- end
+ halt 403 unless key.verify(OpenSSL::Digest.new('SHA256'), signature, comparison)
end
def create object