reattract 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -7
- data/lib/reattract/active_support_include.rb +37 -0
- data/lib/reattract/reattract_error.rb +19 -0
- data/lib/reattract/version.rb +1 -1
- data/lib/reattract/webhook.rb +80 -0
- data/lib/reattract.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf5cfc801f5c995cff77779a49585f2777dec7034be5cb25d42cc972fa42ac9e
|
4
|
+
data.tar.gz: e5aa74ecdeeda05a7d2c98b0f6d83497efcb6b37f16f52736ea18db4ce849dcf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aec4a441a83025aad75c7b94d12b2e56dc75b8b552fb65661eb4be7d8b0591524269f37440110626f7e40b64c083e64c25e6f9ca8218bef187a51ea8342ac046
|
7
|
+
data.tar.gz: 326847fc5d71033e425802520d9a81e23bfa5dcf154af97b9bddc42bcd86cc710dd8b00225808500e64286c6adee2bbdc204e408a2ca61fa9f706efeb31b3a45
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
# Reattract
|
1
|
+
# Reattract Ruby SDK
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Reattract's ruby API
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Install the gem and add to the application's Gemfile by executing:
|
10
8
|
|
11
|
-
$ bundle add
|
9
|
+
$ bundle add reattract
|
12
10
|
|
13
11
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
12
|
|
15
|
-
$ gem install
|
13
|
+
$ gem install reattract
|
16
14
|
|
17
15
|
## Usage
|
18
16
|
|
@@ -26,7 +24,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
26
24
|
|
27
25
|
## Contributing
|
28
26
|
|
29
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
27
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/reattract/sdk-platform.
|
30
28
|
|
31
29
|
## License
|
32
30
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Constant time string comparison, for fixed length strings.
|
4
|
+
# Code borrowed from ActiveSupport
|
5
|
+
# https://github.com/rails/rails/blob/75ac626c4e21129d8296d4206a1960563cc3d4aa/activesupport/lib/active_support/security_utils.rb#L33
|
6
|
+
#
|
7
|
+
# The values compared should be of fixed length, such as strings
|
8
|
+
# that have already been processed by HMAC. Raises in case of length mismatch.
|
9
|
+
module Reattract
|
10
|
+
if defined?(OpenSSL.fixed_length_secure_compare)
|
11
|
+
def fixed_length_secure_compare(a, b)
|
12
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
def fixed_length_secure_compare(a, b)
|
16
|
+
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
17
|
+
|
18
|
+
l = a.unpack "C#{a.bytesize}"
|
19
|
+
|
20
|
+
res = 0
|
21
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
22
|
+
res == 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
module_function :fixed_length_secure_compare
|
26
|
+
|
27
|
+
# Secure string comparison for strings of variable length.
|
28
|
+
#
|
29
|
+
# While a timing attack would not be able to discern the content of
|
30
|
+
# a secret compared via secure_compare, it is possible to determine
|
31
|
+
# the secret length. This should be considered when using secure_compare
|
32
|
+
# to compare weak, short secrets to user input.
|
33
|
+
def secure_compare(a, b)
|
34
|
+
a.length == b.length && fixed_length_secure_compare(a, b)
|
35
|
+
end
|
36
|
+
module_function :secure_compare
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reattract
|
4
|
+
# Standard error message
|
5
|
+
class ReattractError < StandardError
|
6
|
+
attr_reader :message
|
7
|
+
|
8
|
+
def initialize(message = nil)
|
9
|
+
@message = message
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class WebhookVerificationError < ReattractError
|
15
|
+
end
|
16
|
+
|
17
|
+
class WebhookSigningError < ReattractError
|
18
|
+
end
|
19
|
+
end
|
data/lib/reattract/version.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reattract
|
4
|
+
# Reattract's webhook verification class
|
5
|
+
class Webhook
|
6
|
+
|
7
|
+
def self.new_using_raw_bytes(secret)
|
8
|
+
self.new(secret.pack("C*").force_encoding("UTF-8"))
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(secret)
|
12
|
+
if secret.start_with?(SECRET_PREFIX)
|
13
|
+
secret = secret[SECRET_PREFIX.length..-1]
|
14
|
+
end
|
15
|
+
|
16
|
+
@secret = Base64.decode64(secret)
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify(payload, headers)
|
20
|
+
msgId = headers["svix-id"]
|
21
|
+
msgSignature = headers["svix-signature"]
|
22
|
+
msgTimestamp = headers["svix-timestamp"]
|
23
|
+
if !msgSignature || !msgId || !msgTimestamp
|
24
|
+
msgId = headers["webhook-id"]
|
25
|
+
msgSignature = headers["webhook-signature"]
|
26
|
+
msgTimestamp = headers["webhook-timestamp"]
|
27
|
+
if !msgSignature || !msgId || !msgTimestamp
|
28
|
+
raise WebhookVerificationError, "Missing required headers"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
verify_timestamp(msgTimestamp)
|
33
|
+
|
34
|
+
_, signature = sign(msgId, msgTimestamp, payload).split(",", 2)
|
35
|
+
|
36
|
+
passedSignatures = msgSignature.split(" ")
|
37
|
+
passedSignatures.each do |versionedSignature|
|
38
|
+
version, expectedSignature = versionedSignature.split(",", 2)
|
39
|
+
if version != "v1"
|
40
|
+
next
|
41
|
+
end
|
42
|
+
if ::Reattract::secure_compare(signature, expectedSignature)
|
43
|
+
return JSON.parse(payload, symbolize_names: true)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
raise WebhookVerificationError, "No matching signature found"
|
47
|
+
end
|
48
|
+
|
49
|
+
def sign(msgId, timestamp, payload)
|
50
|
+
begin
|
51
|
+
now = Integer(timestamp)
|
52
|
+
rescue
|
53
|
+
raise WebhookSigningError, "Invalid timestamp"
|
54
|
+
end
|
55
|
+
toSign = "#{msgId}.#{timestamp}.#{payload}"
|
56
|
+
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), @secret, toSign)).strip
|
57
|
+
return "v1,#{signature}"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
SECRET_PREFIX = "whsec_"
|
62
|
+
TOLERANCE = 5 * 60
|
63
|
+
|
64
|
+
def verify_timestamp(timestampHeader)
|
65
|
+
begin
|
66
|
+
now = Integer(Time.now)
|
67
|
+
timestamp = Integer(timestampHeader)
|
68
|
+
rescue
|
69
|
+
raise WebhookVerificationError, "Invalid Signature Headers"
|
70
|
+
end
|
71
|
+
|
72
|
+
if timestamp < (now - TOLERANCE)
|
73
|
+
raise WebhookVerificationError, "Message timestamp too old"
|
74
|
+
end
|
75
|
+
if timestamp > (now + TOLERANCE)
|
76
|
+
raise WebhookVerificationError, "Message timestamp too new"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/reattract.rb
CHANGED
@@ -2,11 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
4
|
|
5
|
+
require_relative 'reattract/active_support_include'
|
6
|
+
require_relative 'reattract/reattract_error'
|
5
7
|
require_relative 'reattract/version'
|
6
8
|
require_relative 'reattract/configuration'
|
7
9
|
require_relative 'reattract/jwt_generator'
|
8
10
|
require_relative 'reattract/connection'
|
9
11
|
require_relative 'reattract/request'
|
12
|
+
require_relative 'reattract/webhook'
|
13
|
+
|
14
|
+
# resources
|
10
15
|
require_relative 'reattract/resources/app_event'
|
11
16
|
require_relative 'reattract/resources/campaign'
|
12
17
|
require_relative 'reattract/resources/customer'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reattract
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Burris
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -41,9 +41,11 @@ files:
|
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
43
|
- lib/reattract.rb
|
44
|
+
- lib/reattract/active_support_include.rb
|
44
45
|
- lib/reattract/configuration.rb
|
45
46
|
- lib/reattract/connection.rb
|
46
47
|
- lib/reattract/jwt_generator.rb
|
48
|
+
- lib/reattract/reattract_error.rb
|
47
49
|
- lib/reattract/request.rb
|
48
50
|
- lib/reattract/resources/app_event.rb
|
49
51
|
- lib/reattract/resources/campaign.rb
|
@@ -52,6 +54,7 @@ files:
|
|
52
54
|
- lib/reattract/resources/invite_conversion.rb
|
53
55
|
- lib/reattract/resources/invite_session.rb
|
54
56
|
- lib/reattract/version.rb
|
57
|
+
- lib/reattract/webhook.rb
|
55
58
|
- sig/reattract/api.rbs
|
56
59
|
homepage: https://docs.reattract.io
|
57
60
|
licenses:
|