fopost 0.2.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +169 -0
- data/README.md +593 -3
- data/lib/fopost/client.rb +29 -1
- data/lib/fopost/errors.rb +25 -0
- data/lib/fopost/file_part.rb +73 -0
- data/lib/fopost/http/client.rb +36 -4
- data/lib/fopost/http/multipart.rb +38 -0
- data/lib/fopost/models.rb +1855 -28
- data/lib/fopost/platforms.rb +27 -0
- data/lib/fopost/resources/account_groups.rb +37 -0
- data/lib/fopost/resources/accounts.rb +445 -3
- data/lib/fopost/resources/activity.rb +27 -0
- data/lib/fopost/resources/ads.rb +910 -7
- data/lib/fopost/resources/analytics.rb +63 -0
- data/lib/fopost/resources/blog.rb +206 -0
- data/lib/fopost/resources/blogs.rb +115 -0
- data/lib/fopost/resources/broadcasts.rb +189 -0
- data/lib/fopost/resources/contacts.rb +136 -0
- data/lib/fopost/resources/google_ads.rb +346 -0
- data/lib/fopost/resources/google_business.rb +166 -0
- data/lib/fopost/resources/inbox.rb +73 -7
- data/lib/fopost/resources/knowledge.rb +74 -0
- data/lib/fopost/resources/media.rb +56 -0
- data/lib/fopost/resources/posts.rb +75 -4
- data/lib/fopost/resources/validate.rb +37 -0
- data/lib/fopost/resources/whatsapp.rb +255 -0
- data/lib/fopost/version.rb +1 -1
- data/lib/fopost/webhooks.rb +97 -0
- data/lib/fopost.rb +2 -0
- metadata +17 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'time'
|
|
6
|
+
|
|
7
|
+
require 'fopost/models'
|
|
8
|
+
|
|
9
|
+
module Fopost
|
|
10
|
+
# Raised when a webhook delivery cannot be trusted.
|
|
11
|
+
class WebhookSignatureError < StandardError; end
|
|
12
|
+
|
|
13
|
+
# Verifies the signature FoPost puts on every outbound webhook, including
|
|
14
|
+
# the website target of the blog.
|
|
15
|
+
#
|
|
16
|
+
# event = Fopost::Webhooks.verify(request.raw_post, request.headers, secret)
|
|
17
|
+
# event.article.title if event.event == 'article.published'
|
|
18
|
+
#
|
|
19
|
+
# Verify the raw request bytes, never re-serialised JSON: key order and
|
|
20
|
+
# whitespace change the bytes and the signature no longer matches.
|
|
21
|
+
module Webhooks
|
|
22
|
+
SIGNATURE_HEADER = 'x-fopost-signature'
|
|
23
|
+
TIMESTAMPED_SIGNATURE_HEADER = 'x-fopost-signature-256'
|
|
24
|
+
TIMESTAMP_HEADER = 'x-fopost-timestamp'
|
|
25
|
+
EVENT_HEADER = 'x-fopost-event'
|
|
26
|
+
DELIVERY_HEADER = 'x-fopost-delivery'
|
|
27
|
+
DEFAULT_TOLERANCE = 300
|
|
28
|
+
|
|
29
|
+
module_function
|
|
30
|
+
|
|
31
|
+
# Returns the parsed envelope as a {Fopost::WebhookEvent}, or raises
|
|
32
|
+
# {Fopost::WebhookSignatureError}.
|
|
33
|
+
#
|
|
34
|
+
# The timestamped signature is checked when `X-FoPost-Timestamp` is
|
|
35
|
+
# present and rejected when it is older than `tolerance` seconds; the
|
|
36
|
+
# plain body signature is accepted only when no timestamp was sent.
|
|
37
|
+
# `headers` may use any casing; a Rack `HTTP_X_FOPOST_*` key also works.
|
|
38
|
+
def verify(raw_body, headers, secret, tolerance: DEFAULT_TOLERANCE, now: nil)
|
|
39
|
+
raise WebhookSignatureError, 'fopost: a webhook secret is required' if secret.nil? || secret.to_s.empty?
|
|
40
|
+
|
|
41
|
+
body = raw_body.to_s
|
|
42
|
+
lookup = normalize_headers(headers)
|
|
43
|
+
timestamp = lookup[TIMESTAMP_HEADER]
|
|
44
|
+
|
|
45
|
+
if timestamp
|
|
46
|
+
check_timestamp(timestamp, tolerance, now)
|
|
47
|
+
expected = sign("#{timestamp}.#{body}", secret)
|
|
48
|
+
match = secure_equal(lookup[TIMESTAMPED_SIGNATURE_HEADER], expected)
|
|
49
|
+
else
|
|
50
|
+
match = secure_equal(lookup[SIGNATURE_HEADER], sign(body, secret))
|
|
51
|
+
end
|
|
52
|
+
raise WebhookSignatureError, 'fopost: webhook signature mismatch' unless match
|
|
53
|
+
|
|
54
|
+
parse(body)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The header value FoPost sends for these exact bytes.
|
|
58
|
+
def sign(payload, secret)
|
|
59
|
+
"sha256=#{OpenSSL::HMAC.hexdigest('SHA256', secret.to_s, payload)}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def check_timestamp(timestamp, tolerance, now)
|
|
63
|
+
seconds = Integer(timestamp.to_s.strip, exception: false)
|
|
64
|
+
raise WebhookSignatureError, 'fopost: webhook timestamp is not a number' unless seconds
|
|
65
|
+
|
|
66
|
+
drift = ((now || Time.now).to_i - seconds).abs
|
|
67
|
+
raise WebhookSignatureError, 'fopost: webhook timestamp outside tolerance' if drift > tolerance
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def secure_equal(given, expected)
|
|
71
|
+
return false if given.nil?
|
|
72
|
+
|
|
73
|
+
given = given.to_s.strip
|
|
74
|
+
return false unless given.bytesize == expected.bytesize
|
|
75
|
+
|
|
76
|
+
OpenSSL.fixed_length_secure_compare(given, expected)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def parse(body)
|
|
80
|
+
data = JSON.parse(body)
|
|
81
|
+
raise WebhookSignatureError, 'fopost: webhook body is not an object' unless data.is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
WebhookEvent.new(data)
|
|
84
|
+
rescue JSON::ParserError
|
|
85
|
+
raise WebhookSignatureError, 'fopost: webhook body is not JSON'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def normalize_headers(headers)
|
|
89
|
+
(headers || {}).each_with_object({}) do |(key, value), out|
|
|
90
|
+
name = key.to_s.downcase.tr('_', '-').sub(/\Ahttp-/, '')
|
|
91
|
+
out[name] = value.is_a?(Array) ? value.first : value
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private_class_method :check_timestamp, :secure_equal, :parse, :normalize_headers
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/fopost.rb
CHANGED
|
@@ -6,11 +6,13 @@ require 'fopost/unset'
|
|
|
6
6
|
require 'fopost/platforms'
|
|
7
7
|
require 'fopost/model'
|
|
8
8
|
require 'fopost/models'
|
|
9
|
+
require 'fopost/file_part'
|
|
9
10
|
require 'fopost/http/response'
|
|
10
11
|
require 'fopost/http/transport'
|
|
11
12
|
require 'fopost/http/net_http_transport'
|
|
12
13
|
require 'fopost/http/client'
|
|
13
14
|
require 'fopost/client'
|
|
15
|
+
require 'fopost/webhooks'
|
|
14
16
|
|
|
15
17
|
# Official Ruby SDK for the FoPost API.
|
|
16
18
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fopost
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- FoPost
|
|
@@ -22,23 +22,39 @@ files:
|
|
|
22
22
|
- lib/fopost.rb
|
|
23
23
|
- lib/fopost/client.rb
|
|
24
24
|
- lib/fopost/errors.rb
|
|
25
|
+
- lib/fopost/file_part.rb
|
|
25
26
|
- lib/fopost/http/client.rb
|
|
27
|
+
- lib/fopost/http/multipart.rb
|
|
26
28
|
- lib/fopost/http/net_http_transport.rb
|
|
27
29
|
- lib/fopost/http/response.rb
|
|
28
30
|
- lib/fopost/http/transport.rb
|
|
29
31
|
- lib/fopost/model.rb
|
|
30
32
|
- lib/fopost/models.rb
|
|
31
33
|
- lib/fopost/platforms.rb
|
|
34
|
+
- lib/fopost/resources/account_groups.rb
|
|
32
35
|
- lib/fopost/resources/accounts.rb
|
|
36
|
+
- lib/fopost/resources/activity.rb
|
|
33
37
|
- lib/fopost/resources/ads.rb
|
|
34
38
|
- lib/fopost/resources/ai.rb
|
|
39
|
+
- lib/fopost/resources/analytics.rb
|
|
35
40
|
- lib/fopost/resources/base.rb
|
|
41
|
+
- lib/fopost/resources/blog.rb
|
|
42
|
+
- lib/fopost/resources/blogs.rb
|
|
43
|
+
- lib/fopost/resources/broadcasts.rb
|
|
44
|
+
- lib/fopost/resources/contacts.rb
|
|
45
|
+
- lib/fopost/resources/google_ads.rb
|
|
46
|
+
- lib/fopost/resources/google_business.rb
|
|
36
47
|
- lib/fopost/resources/inbox.rb
|
|
48
|
+
- lib/fopost/resources/knowledge.rb
|
|
37
49
|
- lib/fopost/resources/labels.rb
|
|
50
|
+
- lib/fopost/resources/media.rb
|
|
38
51
|
- lib/fopost/resources/posts.rb
|
|
52
|
+
- lib/fopost/resources/validate.rb
|
|
53
|
+
- lib/fopost/resources/whatsapp.rb
|
|
39
54
|
- lib/fopost/resources/workspaces.rb
|
|
40
55
|
- lib/fopost/unset.rb
|
|
41
56
|
- lib/fopost/version.rb
|
|
57
|
+
- lib/fopost/webhooks.rb
|
|
42
58
|
homepage: https://fopost.com
|
|
43
59
|
licenses:
|
|
44
60
|
- MIT
|