From 056d0e507b0b11416e57493b45a054bf9e6dfc11 Mon Sep 17 00:00:00 2001 From: Brandur Date: Mon, 27 Apr 2020 14:29:08 -0700 Subject: [PATCH] Add new `.generate_header` method for webhooks Adds a new `generate_header` method for the webhooks module, following up #915. This method doesn't help in any way with webhook verification, but may be useful to users in their test suites as it allows them to easily simulate the contents of a header that Stripe might have sent. We briefly discussed an alternative design here, but this one seems like the best fit: https://github.com/stripe/stripe-ruby/pull/915#issuecomment-620164654 --- lib/stripe/webhook.rb | 17 +++++++++++++++++ test/stripe/webhook_test.rb | 24 +++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/stripe/webhook.rb b/lib/stripe/webhook.rb index e11eace26..df1ed65d3 100644 --- a/lib/stripe/webhook.rb +++ b/lib/stripe/webhook.rb @@ -39,6 +39,23 @@ def self.compute_signature(timestamp, payload, secret) timestamped_payload) end + # Generates a value that would be added to a `Stripe-Signature` for a + # given webhook payload. + # + # Note that this isn't needed to verify webhooks in any way, and is + # mainly here for use in test cases (those that are both within this + # project and without). + def self.generate_header(timestamp, signature, scheme: EXPECTED_SCHEME) + raise ArgumentError, "timestamp should be an instance of Time" \ + unless timestamp.is_a?(Time) + raise ArgumentError, "signature should be a string" \ + unless signature.is_a?(String) + raise ArgumentError, "scheme should be a string" \ + unless scheme.is_a?(String) + + "t=#{timestamp.to_i},#{scheme}=#{signature}" + end + # Extracts the timestamp and the signature(s) with the desired scheme # from the header def self.get_timestamp_and_signatures(header, scheme) diff --git a/test/stripe/webhook_test.rb b/test/stripe/webhook_test.rb index f22dd13d5..56a8d371e 100644 --- a/test/stripe/webhook_test.rb +++ b/test/stripe/webhook_test.rb @@ -22,7 +22,11 @@ def generate_header(opts = {}) opts[:payload], opts[:secret] ) - "t=#{opts[:timestamp].to_i},#{opts[:scheme]}=#{opts[:signature]}" + Stripe::Webhook::Signature.generate_header( + opts[:timestamp], + opts[:signature], + scheme: opts[:scheme] + ) end context ".compute_signature" do @@ -38,6 +42,24 @@ def generate_header(opts = {}) end end + context ".generate_header" do + should "generate a header in valid format" do + timestamp = Time.now + signature = Stripe::Webhook::Signature.compute_signature( + timestamp, + EVENT_PAYLOAD, + SECRET + ) + scheme = "v1" + header = Stripe::Webhook::Signature.generate_header( + timestamp, + signature, + scheme: scheme + ) + assert_equal("t=#{timestamp.to_i},#{scheme}=#{signature}", header) + end + end + context ".construct_event" do should "return an Event instance from a valid JSON payload and valid signature header" do header = generate_header