add hmac webhook secret
This commit is contained in:
@@ -15,11 +15,7 @@ module SendWebhookRequest
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def call(webhook_url, event_uuid:, event_type:, record:, data:, attempt: 0)
|
||||
uri = begin
|
||||
URI(webhook_url.url)
|
||||
rescue URI::Error
|
||||
Addressable::URI.parse(webhook_url.url).normalize
|
||||
end
|
||||
uri = parse_uri(webhook_url.url)
|
||||
|
||||
if Docuseal.multitenant?
|
||||
raise HttpsError, 'Only HTTPS is allowed.' if (uri.scheme != 'https' || [443, nil].exclude?(uri.port)) &&
|
||||
@@ -43,6 +39,8 @@ module SendWebhookRequest
|
||||
data: data
|
||||
}.to_json
|
||||
|
||||
req.headers['X-Docuseal-Signature'] = WebhookUrls::Signatures.sign(webhook_url.hmac_secret, body: req.body)
|
||||
|
||||
req.options.read_timeout = 15
|
||||
req.options.open_timeout = 8
|
||||
end
|
||||
@@ -55,6 +53,12 @@ module SendWebhookRequest
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
def parse_uri(url)
|
||||
URI(url)
|
||||
rescue URI::Error
|
||||
Addressable::URI.parse(url).normalize
|
||||
end
|
||||
|
||||
def create_webhook_event(webhook_url, event_uuid:, event_type:, record:)
|
||||
return if event_uuid.blank?
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WebhookUrls
|
||||
module Signatures
|
||||
SECRET_PREFIX = 'whsec_'
|
||||
SECRET_BYTES = 24
|
||||
TOLERANCE = 5 * 60
|
||||
|
||||
InvalidSignatureError = Class.new(StandardError)
|
||||
TimestampError = Class.new(StandardError)
|
||||
|
||||
module_function
|
||||
|
||||
def generate_secret
|
||||
SECRET_PREFIX + Base64.strict_encode64(SecureRandom.bytes(SECRET_BYTES))
|
||||
end
|
||||
|
||||
def sign(secret, body:, timestamp: Time.current.to_i)
|
||||
"#{timestamp}.#{OpenSSL::HMAC.hexdigest('sha256', secret, "#{timestamp}.#{body}")}"
|
||||
end
|
||||
|
||||
def verify(secret, body:, header:, tolerance: TOLERANCE)
|
||||
ts, sig = header.to_s.split('.', 2)
|
||||
ts = Integer(ts, exception: false)
|
||||
|
||||
raise InvalidSignatureError unless ts && sig
|
||||
|
||||
now = Time.current.to_i
|
||||
|
||||
raise TimestampError, 'Too old' if ts < now - tolerance
|
||||
raise TimestampError, 'In future' if ts > now + tolerance
|
||||
|
||||
expected = OpenSSL::HMAC.hexdigest('sha256', secret, "#{ts}.#{body}")
|
||||
|
||||
raise InvalidSignatureError unless ActiveSupport::SecurityUtils.secure_compare(expected, sig)
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user