1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
|
require 'flipper/cloud/message_verifier'
RSpec.describe Flipper::Cloud::MessageVerifier do
let(:payload) { "some payload" }
let(:secret) { "secret" }
let(:timestamp) { Time.now }
describe "#generate" do
it "generates signature that can be verified" do
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret)
signature = message_verifier.generate(payload, timestamp)
header = generate_header(timestamp: timestamp, signature: signature)
expect(message_verifier.verify(payload, header)).to be(true)
end
end
describe "#header" do
it "generates a header in valid format" do
version = "v1"
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret, version: version)
signature = message_verifier.generate(payload, timestamp)
header = message_verifier.header(signature, timestamp)
expect(header).to eq("t=#{timestamp.to_i},#{version}=#{signature}")
end
end
describe ".header" do
it "generates a header in valid format" do
version = "v1"
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret, version: version)
signature = message_verifier.generate(payload, timestamp)
header = Flipper::Cloud::MessageVerifier.header(signature, timestamp, version)
expect(header).to eq("t=#{timestamp.to_i},#{version}=#{signature}")
end
end
describe "#verify" do
it "raises a InvalidSignature when the header does not have the expected format" do
header = "i'm not even a real signature header"
expect {
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret")
message_verifier.verify(payload, header)
}.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, "Unable to extract timestamp and signatures from header")
end
it "raises a InvalidSignature when there are no signatures with the expected version" do
header = generate_header(version: "v0")
expect {
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret")
message_verifier.verify(payload, header)
}.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, /No signatures found with expected version/)
end
it "raises a InvalidSignature when there are no valid signatures for the payload" do
header = generate_header(signature: "bad_signature")
expect {
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret")
message_verifier.verify(payload, header)
}.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, "No signatures found matching the expected signature for payload")
end
it "raises a InvalidSignature when the timestamp is not within the tolerance" do
header = generate_header(timestamp: Time.now - 15)
expect {
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret)
message_verifier.verify(payload, header, tolerance: 10)
}.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, /Timestamp outside the tolerance zone/)
end
it "returns true when the header contains a valid signature and the timestamp is within the tolerance" do
header = generate_header
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret")
expect(message_verifier.verify(payload, header, tolerance: 10)).to be(true)
end
it "returns true when the header contains at least one valid signature" do
header = generate_header + ",v1=bad_signature"
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret)
expect(message_verifier.verify(payload, header, tolerance: 10)).to be(true)
end
it "returns true when the header contains a valid signature and the timestamp is off but no tolerance is provided" do
header = generate_header(timestamp: Time.at(12_345))
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret)
expect(message_verifier.verify(payload, header)).to be(true)
end
end
private
def generate_header(options = {})
options[:secret] ||= secret
options[:version] ||= "v1"
message_verifier = Flipper::Cloud::MessageVerifier.new(secret: options[:secret], version: options[:version])
options[:timestamp] ||= timestamp
options[:payload] ||= payload
options[:signature] ||= message_verifier.generate(options[:payload], options[:timestamp])
Flipper::Cloud::MessageVerifier.header(options[:signature], options[:timestamp], options[:version])
end
end
|