summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpdp8 <pdp8@pdp8.info>2023-04-23 19:26:00 +0200
committerpdp8 <pdp8@pdp8.info>2023-04-23 19:26:00 +0200
commit79e3bf726b7166c5ec641ea02b75f82a74f31698 (patch)
tree2e68564452dc206e5845758bc8e352d7a1e31f3c
parentb2b88253f5cc4221b01ed860b02ae156941e03ce (diff)
inbox/outbox GET
-rw-r--r--application.rb184
-rw-r--r--config.ru1
-rw-r--r--outbox/230401T23:00:01.json4
-rw-r--r--outbox/230402T23:00:01.json10
-rw-r--r--pdp842
-rwxr-xr-xpost.rb17
-rw-r--r--webfinger10
7 files changed, 155 insertions, 113 deletions
diff --git a/application.rb b/application.rb
index 7618855..6e700fd 100644
--- a/application.rb
+++ b/application.rb
@@ -14,6 +14,9 @@ ACTOR = URI.join(SOCIAL_URL, USER)
MATRIX = "@#{USER}:matrix.#{WWW_DOMAIN}"
+OUTBOX = File.join(File.dirname(__FILE__), "outbox")
+INBOX = File.join(File.dirname(__FILE__), "inbox")
+
class Application
def call(env)
code = 404
@@ -24,41 +27,21 @@ class Application
when 'POST'
case env["REQUEST_URI"]
- when "/inbox"
- type = "text/plain"
- signature_header = {}
- if env["HTTP_SIGNATURE"].split(',')
- 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.urlsafe_decode64(signature_header['signature'].encode("ascii-8bit"))
- 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.encode("ascii-8bit"))
- input = JSON.parse(env["rack.input"].gets)
- # p input
- code = 200
- response = "OK"
- else
- code = 401
- response = 'Request signature could not be verified'
- end
+ when "/inbox" # receive from server
+ if verify(env)
+ input = JSON.parse(env["rack.input"].gets)
+ # p input["type"]
+ code = 200
+ response = "OK"
else
code = 401
- response = 'Request signature could not be verified'
+ response = "not verified"
+ end
+
+ when "/outbox"
+ input = JSON.parse(env["rack.input"].gets)
+ case input["type"]
+ when "Create"
end
end
@@ -68,95 +51,72 @@ class Application
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
- }
- ]
- }
+ response = File.read("webfinger")
+ code = 200
when "/#{USER}"
+ response = File.read("pdp8")
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": "<a href=\"#{WWW_URL}\">#{WWW_DOMAIN}</a>"
- },
- {
- "type": "PropertyValue",
- "name": "Fediverse",
- "value": "<a rel=\"me\" href=\"#{ACTOR}\">@#{ACCOUNT}</a>"
- },
- {
- "type": "PropertyValue",
- "name": "Matrix",
- "value": "<a rel=\"me\" href=\"https://matrix.to/#/#{MATRIX}\">#{MATRIX}</a>"
- }
- ],
- "publicKey" => {
- "@context" => "https://w3id.org/security/v1",
- "@type" => "Key",
- "id" => "#{ACTOR}#main-key",
- "owner" => ACTOR,
- "publicKeyPem" => File.read("public.pem")
- }
- }
when "/outbox"
+ response = ordered_collection OUTBOX
code = 200
+
+ when "/inbox"
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"
+ response = ordered_collection INBOX
+ code = 200
+
+ # when "/following"
+ # when "/followers"
+ # when "/liked"
end
end
- [code, { "Content-Type" => type }, [response.to_json]]
+ [code, { "Content-Type" => type }, [response]]
+ end
+
+ def ordered_collection dir
+ posts = Dir[File.join(dir, "*.json")].sort.reverse.collect { |f| p f; JSON.parse(File.read f) }
+ {
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "summary" => "pdp8 outbox",
+ "type" => "OrderedCollection",
+ "totalItems" => posts.size,
+ "orderedItems" => posts,
+ }.to_json
+ end
+
+ def verify(env)
+ begin
+ 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.urlsafe_decode64(signature_header['signature'].encode("ascii-8bit"))
+ 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.encode("ascii-8bit"))
+ true
+ else
+ false
+ end
+ rescue
+ false
+ end
end
end
diff --git a/config.ru b/config.ru
index 535b79a..22f309b 100644
--- a/config.ru
+++ b/config.ru
@@ -2,4 +2,5 @@ require_relative './application.rb'
require 'rack/protection'
use Rack::Protection, :except => :session_hijacking
use Rack::Reloader
+# use Verify
run Application.new
diff --git a/outbox/230401T23:00:01.json b/outbox/230401T23:00:01.json
new file mode 100644
index 0000000..baeb7f8
--- /dev/null
+++ b/outbox/230401T23:00:01.json
@@ -0,0 +1,4 @@
+{
+ "type": "Note",
+ "name": "A Simple Note"
+}
diff --git a/outbox/230402T23:00:01.json b/outbox/230402T23:00:01.json
new file mode 100644
index 0000000..bd50adb
--- /dev/null
+++ b/outbox/230402T23:00:01.json
@@ -0,0 +1,10 @@
+{
+ "type": "Note",
+ "name": "Another Simple Note",
+ "tag": [
+ {
+ "type": "Hashtag",
+ "name": "#test"
+ }
+ ]
+}
diff --git a/pdp8 b/pdp8
new file mode 100644
index 0000000..ceb25c9
--- /dev/null
+++ b/pdp8
@@ -0,0 +1,42 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams"
+ ],
+ "id": "https://social.pdp8.info/pdp8",
+ "type": "Person",
+ "preferredUsername": "pdp8",
+ "name": "pdp8",
+ "inbox": "https://social.pdp8.info/inbox",
+ "outbox": "https://social.pdp8.info/outbox",
+ "following": "https://social.pdp8.info/following",
+ "followers": "https://social.pdp8.info/followers",
+ "liked": "https://social.pdp8.info/liked",
+ "icon": {
+ "type": "Image",
+ "url": "https://pdp8.info/pdp8.png"
+ },
+ "attachment": [
+ {
+ "type": "PropertyValue",
+ "name": "Web",
+ "value": "<a href=\"https://pdp8.info\">pdp8.info</a>"
+ },
+ {
+ "type": "PropertyValue",
+ "name": "Fediverse",
+ "value": "<a rel=\"me\" href=\"https://social.pdp8.info/pdp8\">@pdp8@social.pdp8.info</a>"
+ },
+ {
+ "type": "PropertyValue",
+ "name": "Matrix",
+ "value": "<a rel=\"me\" href=\"https://matrix.to/#/@pdp8:matrix.pdp8.info\">@pdp8:matrix.pdp8.info</a>"
+ }
+ ],
+ "publicKey": {
+ "@context": "https://w3id.org/security/v1",
+ "@type": "Key",
+ "id": "https://social.pdp8.info/pdp8#main-key",
+ "owner": "https://social.pdp8.info/pdp8",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4pZMYXoh8G+iEguDpKGD\n+eq+uDdhx/ch2x7rq00aPDDeHp40CG8bW1ZRC4WIOTUOgK4MeMDoaXT9/vWgr7xT\n/Qm95SEyZWBKqasBsp2uGkDxl23C6dB2eeshuAwt308Qzm2DeTrKPAw/XBAyWHDD\nfan2nWrtXcDJaeXhD/QE/w7Qiz5F2GCb/E/o46SwEyOJi13WxI9Jtuzh76xmwNsd\nwVWIBSu4zn0hg/wv+xtq/c/KLO4ZL54YiJXxRwrkDN7Xdnd18FwFuZ7fT8+kfiqF\nBnvle0OTKxumW46U7ivaylnqoSOvsYK6oyop/m2rl9Nh3sGdcmOsLoFVDg4gOjDf\niQIDAQAB\n-----END PUBLIC KEY-----\n"
+ }
+}
diff --git a/post.rb b/post.rb
index ed7a964..c5e7cd7 100755
--- a/post.rb
+++ b/post.rb
@@ -6,7 +6,21 @@ require 'base64'
require 'net/http'
require 'uri'
-document = { "a" => 2 } # .to_json
+# document = { "a" => 2 } # .to_json
+document = {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "type": "Like",
+ "actor": "https://example.net/~mallory",
+ "to": ["https://hatchat.example/sarah/",
+ "https://example.com/peeps/john/"],
+ "object": {
+ "@context": { "@language": "en" },
+ "id": "https://example.org/~alice/note/23",
+ "type": "Note",
+ "attributedTo": "https://example.org/~alice",
+ "content": "I'm a goat"
+ }
+}
date = Time.now.utc.httpdate
keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
signed_string = "(request-target): post /inbox\nhost: social.pdp8.info\ndate: #{date}"
@@ -19,6 +33,7 @@ http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
header = {
+ 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'Host' => 'social.pdp8.info',
'Date' => date,
'Signature' => signed_header,
diff --git a/webfinger b/webfinger
new file mode 100644
index 0000000..507b34e
--- /dev/null
+++ b/webfinger
@@ -0,0 +1,10 @@
+{
+ "subject": "acct:pdp8@social.pdp8.info",
+ "links": [
+ {
+ "rel": "self",
+ "type": "application/activity+json",
+ "href": "https://social.pdp8.info/pdp8"
+ }
+ ]
+}