Add integration test for delivering signed payloads

parent 77006e7b
Pipeline #348 failed with stage
in 29 seconds
......@@ -56,3 +56,150 @@ def request(method, resource, headers = nil, body = nil)
{response.status_code, response_body.to_s, response.headers}
end
private alias HTTPSignature = PubRelay::WebServer::HTTPSignature
module WebMock
class_getter registry
end
private class IntegrationTest
getter domain_keys = Hash(String, OpenSSL::RSA).new
def initialize
@pub_relay = PubRelay.new("relay.com", SPEC_PKEY, SPEC_REDIS, "localhost", 0)
@pub_relay.spawn
relay_actor = HTTPSignature::Actor.new(
id: "https://relay.com/actor",
public_key: HTTPSignature::Key.new(
public_key_pem: SPEC_PKEY.public_key.to_pem,
owner: "https://relay.com/actor"
),
endpoints: nil,
inbox: "https://relay.com/inbox"
)
WebMock.stub("GET", "https://relay.com/actor")
.to_return(body: relay_actor.to_json)
end
def request(method, resource, headers = nil, body = nil)
request = HTTP::Request.new(method, resource, headers, body)
response = HTTP::Server::Response.new(IO::Memory.new)
response_body = IO::Memory.new
response.output = response_body
context = HTTP::Server::Context.new(request, response)
@pub_relay.web_server.call(context)
{response.status_code, response_body.to_s, response.headers}
end
def add_domain(domain)
domain_keys[domain] = OpenSSL::RSA.new(512)
domain_actor = HTTPSignature::Actor.new(
id: "https://#{domain}/actor",
public_key: HTTPSignature::Key.new(
public_key_pem: domain_keys[domain].public_key.to_pem,
owner: "https://#{domain}/actor"
),
endpoints: nil,
inbox: "https://#{domain}/inbox"
)
WebMock.stub("GET", "https://#{domain}/actor")
.to_return(body: domain_actor.to_json)
end
def signed_inbox_request(*, body : String, from source_domain : String, headers = HTTP::Headers.new)
body_hash = OpenSSL::Digest.new("sha256")
body_hash.update(body)
body_hash = Base64.strict_encode(body_hash.digest)
headers["Host"] = "relay.com"
headers["Date"] = HTTP.format_time(Time.utc_now)
headers["Digest"] = "SHA-256=#{body_hash}"
signed_headers = "(request-target) host date digest"
signed_string = <<-END
(request-target): post /inbox
host: #{headers["Host"]}
date: #{headers["Date"]}
digest: #{headers["Digest"]}
END
signature = domain_keys[source_domain].sign(OpenSSL::Digest.new("sha256"), signed_string)
headers["Signature"] = %(keyId="https://#{source_domain}/actor",headers="#{signed_headers}",signature="#{Base64.strict_encode(signature)}")
request("POST", "/inbox", headers, body)
end
def signed_inbox_request(activity, *, from source_domain : String, headers = HTTP::Headers.new)
signed_inbox_request(body: activity.to_json, from: source_domain, headers: headers)
end
def subscribe_domain(domain)
add_domain(domain) unless domain_keys[domain]?
activity = PubRelay::Activity.new(
id: "https://#{domain}/follows/1",
types: ["Follow"],
object: PubRelay::Activity::PUBLIC_COLLECTION
)
# Collect initial follow Accept
accept_channel = Channel(Nil).new
stub = WebMock.stub("POST", "https://#{domain}/inbox")
.to_return do |request|
activity = PubRelay::Activity.from_json(request.body.not_nil!)
activity.types.should contain("Accept")
activity.object_id.should eq("https://#{domain}/follows/1")
accept_channel.send nil
HTTP::Client::Response.new(202)
end
signed_inbox_request(from: domain, body: activity.to_json)
500.times do
sleep 1.milliseconds
if !accept_channel.empty?
accept_channel.receive
WebMock.registry.@stubs.delete(stub)
sleep 10.milliseconds
return
end
end
raise "Accept not sent for subscribe to #{domain}"
end
def stop
sleep 10.milliseconds
@pub_relay.stop
# Wait up to 500ms to stop
500.times do
sleep 1.milliseconds
break if @pub_relay.stopped?
end
@pub_relay.stopped?.should be_true
end
end
def integration_test
integration_test = IntegrationTest.new
with integration_test yield
integration_test.stop
end
require "../spec_helper"
describe PubRelay::SubscriptionManager::DeliverWorker do
it "delivers signed payloads" do
integration_test do
subscribe_domain("subscriber1.com")
subscribe_domain("subscriber2.com")
activity = PubRelay::Activity.new(
id: "https://subscriber1.com/notes/1/create",
type: "Create",
object: "https://subscriber.com/notes/1",
to: [PubRelay::Activity::PUBLIC_COLLECTION]
)
channel = Channel(Nil).new
WebMock.stub("POST", "https://subscriber2.com/inbox")
.to_return do |request|
body = request.body.not_nil!.gets_to_end
body.should eq(activity.to_json)
request.body.not_nil!.rewind
response = HTTP::Server::Response.new(IO::Memory.new)
context = HTTP::Server::Context.new(request, response)
PubRelay::WebServer::HTTPSignature.new(context).verify_signature
channel.send nil
HTTP::Client::Response.new(202)
end
signed_inbox_request(activity: activity, from: "subscriber1.com")
channel.receive
end
end
end
......@@ -15,6 +15,9 @@ class PubRelay::Activity
def initialize(*, @id, @object, @types, @to = [] of String, @cc = [] of String)
end
def initialize(*, @id, @object, type, @to = [] of String, @cc = [] of String)
@types = [type]
end
def follow?
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment