ahoy_email 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c664f526e52ac8f97cf3d5fe5b154ab6396e3f5c8db3d42c7c76022df9e0b35
4
- data.tar.gz: 063f9ac13bebe763ff2e33677e68bebd75f46837c5e63b920208e2547725a2a9
3
+ metadata.gz: 55896391d55a0158cdf032a2d7a29860984db55a4c116b96317e8986acdef143
4
+ data.tar.gz: 6c1c742d62fa0c2b2b62832dc0030fa7dad31839fc5e015f3806766c14ce9804
5
5
  SHA512:
6
- metadata.gz: 1d7556134bb052f06eea6755f668cdd7c4eb5355dcbe46098894ff6b5ed8735b4c20b240378bafebb9b03b9c2525cf4e7b79e9a0fa240bf1a86289b8c0eb2c2b
7
- data.tar.gz: b576323d7768c3c0872afecbbfa35a7ce12b1d8df9a6dc9877d96e691e1d4675a60ef590e00705be8c245a543dc151249504ba511a96d6bbf37ead17afcf6666
6
+ metadata.gz: 411579c14c08b65b59aaf34dafa6e37e0fdff00acc2232eb9962c31ce7077826aa228d3e7e7b09f657b2e6a2542b003b005e43237106b162ffd60930a13e9cf8
7
+ data.tar.gz: a042562ba6f0a1ed0e342f823bd75b7960fdae2490699f02cbfd1320f81fd82cd19bdaabb437369825701656eb78f5a1663a14ccf918c28605257c3af966dbbf
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 2.3.0 (2024-06-01)
2
+
3
+ - Added support for secret token rotation
4
+ - Improved secret token generation
5
+
1
6
  ## 2.2.0 (2023-07-02)
2
7
 
3
8
  - Removed support for Ruby < 3 and Rails < 6.1
data/README.md CHANGED
@@ -4,9 +4,9 @@ First-party email analytics for Rails
4
4
 
5
5
  :fire: For web and native app analytics, check out [Ahoy](https://github.com/ankane/ahoy)
6
6
 
7
- :bullettrain_side: To manage unsubscribes, check out [Mailkick](https://github.com/ankane/mailkick)
7
+ :bullettrain_side: To manage email subscriptions, check out [Mailkick](https://github.com/ankane/mailkick)
8
8
 
9
- [![Build Status](https://github.com/ankane/ahoy_email/workflows/build/badge.svg?branch=master)](https://github.com/ankane/ahoy_email/actions)
9
+ [![Build Status](https://github.com/ankane/ahoy_email/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/ahoy_email/actions)
10
10
 
11
11
  ## Installation
12
12
 
@@ -106,7 +106,7 @@ user.messages
106
106
  Add extra data to messages. Create a migration like:
107
107
 
108
108
  ```ruby
109
- class AddCouponIdToAhoyMessages < ActiveRecord::Migration[7.0]
109
+ class AddCouponIdToAhoyMessages < ActiveRecord::Migration[7.1]
110
110
  def change
111
111
  add_column :ahoy_messages, :coupon_id, :integer
112
112
  end
@@ -11,24 +11,23 @@ module Ahoy
11
11
  end
12
12
 
13
13
  def click
14
- if params[:id]
15
- # legacy
14
+ legacy = params[:id]
15
+ if legacy
16
16
  token = params[:id].to_s
17
+ campaign = nil
17
18
  url = params[:url].to_s
18
19
  signature = params[:signature].to_s
19
- expected_signature = OpenSSL::HMAC.hexdigest("SHA1", AhoyEmail::Utils.secret_token, url)
20
20
  else
21
21
  token = params[:t].to_s
22
22
  campaign = params[:c].to_s
23
23
  url = params[:u].to_s
24
24
  signature = params[:s].to_s
25
- expected_signature = AhoyEmail::Utils.signature(token: token, campaign: campaign, url: url)
26
25
  end
27
26
 
28
27
  redirect_options = {}
29
28
  redirect_options[:allow_other_host] = true if ActionPack::VERSION::MAJOR >= 7
30
29
 
31
- if ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
30
+ if AhoyEmail::Utils.signature_verified?(legacy: legacy, token: token, campaign: campaign, url: url, signature: signature)
32
31
  data = {}
33
32
  data[:campaign] = campaign if campaign
34
33
  data[:token] = token
@@ -4,6 +4,10 @@ module AhoyEmail
4
4
  class Engine < ::Rails::Engine
5
5
  initializer "ahoy_email" do |app|
6
6
  AhoyEmail.secret_token ||= begin
7
+ tokens = []
8
+ tokens << app.key_generator.generate_key("ahoy_email")
9
+
10
+ # TODO remove in 3.0
7
11
  creds =
8
12
  if app.respond_to?(:credentials) && app.credentials.secret_key_base
9
13
  app.credentials
@@ -15,7 +19,9 @@ module AhoyEmail
15
19
 
16
20
  token = creds.respond_to?(:secret_key_base) ? creds.secret_key_base : creds.secret_token
17
21
  token ||= app.secret_key_base # should come first, but need to maintain backward compatibility
18
- token
22
+ tokens << token
23
+
24
+ tokens
19
25
  end
20
26
  end
21
27
  end
@@ -7,13 +7,28 @@ module AhoyEmail
7
7
  }
8
8
 
9
9
  class << self
10
- def signature(token:, campaign:, url:)
10
+ def signature(token:, campaign:, url:, secret_token: nil)
11
+ secret_token ||= secret_tokens.first
12
+
11
13
  # encode and join with a character outside encoding
12
14
  data = [token, campaign, url].map { |v| Base64.strict_encode64(v.to_s) }.join("|")
13
15
 
14
16
  Base64.urlsafe_encode64(OpenSSL::HMAC.digest("SHA256", secret_token, data), padding: false)
15
17
  end
16
18
 
19
+ def signature_verified?(legacy:, token:, campaign:, url:, signature:)
20
+ secret_tokens.any? do |secret_token|
21
+ expected_signature =
22
+ if legacy
23
+ OpenSSL::HMAC.hexdigest("SHA1", secret_token, url)
24
+ else
25
+ signature(token: token, campaign: campaign, url: url, secret_token: secret_token)
26
+ end
27
+
28
+ ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
29
+ end
30
+ end
31
+
17
32
  def publish(name, event)
18
33
  method_name = "track_#{name}"
19
34
  AhoyEmail.subscribers.each do |subscriber|
@@ -27,8 +42,8 @@ module AhoyEmail
27
42
  end
28
43
  end
29
44
 
30
- def secret_token
31
- AhoyEmail.secret_token || (raise "Secret token is empty")
45
+ def secret_tokens
46
+ Array(AhoyEmail.secret_token || (raise "Secret token is empty"))
32
47
  end
33
48
  end
34
49
  end
@@ -1,3 +1,3 @@
1
1
  module AhoyEmail
2
- VERSION = "2.2.0"
2
+ VERSION = "2.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ahoy_email
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-02 00:00:00.000000000 Z
11
+ date: 2024-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionmailer
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubygems_version: 3.4.10
126
+ rubygems_version: 3.5.9
127
127
  signing_key:
128
128
  specification_version: 4
129
129
  summary: First-party email analytics for Rails