connector-ruby 0.1.1 → 0.3.0

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.
@@ -1,23 +1,117 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openssl"
4
+ require "base64"
5
+ require "json"
4
6
 
5
7
  module ConnectorRuby
6
8
  class WebhookVerifier
9
+ # Verify a WhatsApp Cloud API webhook signature.
10
+ #
11
+ # WhatsApp sends the signature in the `X-Hub-Signature-256` header as
12
+ # `sha256=<hex>`, computed with HMAC-SHA256 over the raw request body
13
+ # using the app secret as the key.
7
14
  def self.verify_whatsapp(payload, signature:, app_secret:)
8
15
  expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", app_secret, payload)}"
9
16
  secure_compare(expected, signature)
10
17
  end
11
18
 
19
+ # Verify a Telegram webhook using the `X-Telegram-Bot-Api-Secret-Token`
20
+ # header configured via `setWebhook`.
12
21
  def self.verify_telegram(token:, payload:, secret_token: nil, header_value: nil)
13
22
  return false unless secret_token && header_value
14
23
  computed = OpenSSL::HMAC.hexdigest("SHA256", secret_token, payload.to_s)
15
24
  secure_compare(computed, header_value.to_s)
16
25
  end
17
26
 
18
- def self.secure_compare(a, b)
19
- a = a.to_s.downcase
20
- b = b.to_s.downcase
27
+ # Verify a Meta Messenger webhook signature.
28
+ #
29
+ # Messenger sends the signature in the `X-Hub-Signature-256` header as
30
+ # `sha256=<hex>`, computed with HMAC-SHA256 over the raw request body
31
+ # using the Facebook app secret as the key.
32
+ #
33
+ # Reference: https://developers.facebook.com/docs/messenger-platform/webhooks#security
34
+ def self.verify_messenger(payload, signature:, app_secret:)
35
+ return false if signature.nil? || app_secret.nil?
36
+ expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", app_secret, payload.to_s)}"
37
+ secure_compare(expected, signature)
38
+ end
39
+
40
+ # Verify a LINE Messaging API webhook signature.
41
+ #
42
+ # LINE sends the signature in the `X-Line-Signature` header as the
43
+ # Base64-encoded HMAC-SHA256 digest of the raw request body, using the
44
+ # channel secret as the key.
45
+ #
46
+ # Because the signature is Base64 (which contains uppercase letters),
47
+ # the comparison is case-sensitive.
48
+ #
49
+ # Reference: https://developers.line.biz/en/reference/messaging-api/#signature-validation
50
+ def self.verify_line(payload, signature:, channel_secret:)
51
+ return false if signature.nil? || channel_secret.nil?
52
+ digest = OpenSSL::HMAC.digest("SHA256", channel_secret, payload.to_s)
53
+ expected = Base64.strict_encode64(digest)
54
+ secure_compare(expected, signature, case_sensitive: true)
55
+ end
56
+
57
+ # Verify a Slack webhook signature with replay protection.
58
+ #
59
+ # Slack sends the signature in `X-Slack-Signature` as `v0=<hex>`, computed
60
+ # as HMAC-SHA256 over the base string `v0:{timestamp}:{body}` using the
61
+ # signing secret as the key. The `X-Slack-Request-Timestamp` value must
62
+ # also be checked for freshness to prevent replay attacks.
63
+ #
64
+ # @param tolerance [Integer] max allowed delta between the timestamp and
65
+ # the current time, in seconds (default 300 = 5 minutes, matching
66
+ # Slack's own recommendation).
67
+ #
68
+ # Reference: https://api.slack.com/authentication/verifying-requests-from-slack
69
+ def self.verify_slack(payload, timestamp:, signature:, signing_secret:, tolerance: 300)
70
+ return false if timestamp.nil? || signature.nil? || signing_secret.nil?
71
+ ts = timestamp.to_i
72
+ return false if ts.zero?
73
+ return false if (Time.now.to_i - ts).abs > tolerance
74
+
75
+ basestring = "v0:#{ts}:#{payload}"
76
+ expected = "v0=#{OpenSSL::HMAC.hexdigest("SHA256", signing_secret, basestring)}"
77
+ secure_compare(expected, signature)
78
+ end
79
+
80
+ # Verify a LiveChat webhook using the shared secret embedded in the body.
81
+ #
82
+ # LiveChat does NOT sign webhooks with HMAC and does NOT use a signature
83
+ # header. Instead, every webhook body contains a `secret_key` field;
84
+ # verification is a constant-time compare between that field and the
85
+ # shared secret you configured in your LiveChat webhook settings.
86
+ #
87
+ # Reference: https://platform.text.com/docs/messaging/webhooks
88
+ # and (production reference) chatbotlic's webhooks_controller.rb
89
+ def self.verify_livechat(payload, expected_secret:)
90
+ return false if expected_secret.nil?
91
+
92
+ data = payload.is_a?(String) ? JSON.parse(payload) : payload
93
+ return false unless data.is_a?(Hash)
94
+
95
+ received = data["secret_key"]
96
+ return false if received.nil?
97
+
98
+ secure_compare(received.to_s, expected_secret.to_s, case_sensitive: true)
99
+ rescue JSON::ParserError
100
+ false
101
+ end
102
+
103
+ # Constant-time string comparison.
104
+ #
105
+ # By default, comparisons are case-insensitive (safe for hex signatures).
106
+ # Pass `case_sensitive: true` for Base64 or shared-secret comparisons where
107
+ # case carries meaning.
108
+ def self.secure_compare(a, b, case_sensitive: false)
109
+ a = a.to_s
110
+ b = b.to_s
111
+ unless case_sensitive
112
+ a = a.downcase
113
+ b = b.downcase
114
+ end
21
115
  return false unless a.bytesize == b.bytesize
22
116
 
23
117
  OpenSSL.fixed_length_secure_compare(a, b)
@@ -10,6 +10,12 @@ require_relative "connector_ruby/webhook_verifier"
10
10
  require_relative "connector_ruby/channels/base"
11
11
  require_relative "connector_ruby/channels/whatsapp"
12
12
  require_relative "connector_ruby/channels/telegram"
13
+ require_relative "connector_ruby/channels/messenger"
14
+ require_relative "connector_ruby/channels/line"
15
+ require_relative "connector_ruby/channels/slack"
16
+ require_relative "connector_ruby/channels/livechat"
17
+ require_relative "connector_ruby/batch_sender"
18
+ require_relative "connector_ruby/delivery_tracker"
13
19
 
14
20
  module ConnectorRuby
15
21
  class << self
@@ -29,4 +35,8 @@ module ConnectorRuby
29
35
  # Convenience aliases
30
36
  WhatsApp = Channels::WhatsApp
31
37
  Telegram = Channels::Telegram
38
+ Messenger = Channels::Messenger
39
+ Line = Channels::Line
40
+ Slack = Channels::Slack
41
+ LiveChat = Channels::LiveChat
32
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: connector-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johannes Dwi Cahyo
@@ -9,6 +9,20 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.2'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: minitest
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -52,7 +66,8 @@ dependencies:
52
66
  - !ruby/object:Gem::Version
53
67
  version: '3.0'
54
68
  description: Framework-agnostic SDK for sending/receiving messages across chat platforms.
55
- Supports WhatsApp Cloud API, Telegram Bot API, and more.
69
+ Supports WhatsApp Cloud API, Telegram Bot API, Facebook Messenger, LINE Messaging
70
+ API, Slack Web API, and LiveChat Agent API.
56
71
  email:
57
72
  - johannes@example.com
58
73
  executables: []
@@ -65,10 +80,16 @@ files:
65
80
  - Rakefile
66
81
  - connector-ruby.gemspec
67
82
  - lib/connector_ruby.rb
83
+ - lib/connector_ruby/batch_sender.rb
68
84
  - lib/connector_ruby/channels/base.rb
85
+ - lib/connector_ruby/channels/line.rb
86
+ - lib/connector_ruby/channels/livechat.rb
87
+ - lib/connector_ruby/channels/messenger.rb
88
+ - lib/connector_ruby/channels/slack.rb
69
89
  - lib/connector_ruby/channels/telegram.rb
70
90
  - lib/connector_ruby/channels/whatsapp.rb
71
91
  - lib/connector_ruby/configuration.rb
92
+ - lib/connector_ruby/delivery_tracker.rb
72
93
  - lib/connector_ruby/error.rb
73
94
  - lib/connector_ruby/event.rb
74
95
  - lib/connector_ruby/http_client.rb