Module: OpenWire::Webhook

Defined in:
lib/open_wire/webhook.rb

Overview

Verify and parse inbound POSTs from the Open-Wire gateway.

Constant Summary collapse

HEADER_PROTOCOL =
"X-Open-Wire-Protocol"
HEADER_INSTALLATION =
"X-Open-Wire-Installation"
HEADER_SECRET =
"X-Open-Wire-Secret"

Class Method Summary collapse

Class Method Details

.normalize_headers(headers) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/open_wire/webhook.rb', line 33

def normalize_headers(headers)
  out = {}
  headers.each do |key, value|
    name = key.to_s
    name = name.sub(/\AHTTP_/, "").tr("_", "-") if name.start_with?("HTTP_")
    out[name.downcase] = value.to_s
    out[name] = value.to_s
  end
  # ActionDispatch::Http::Headers style
  if headers.respond_to?(:[])
    [HEADER_SECRET, HEADER_PROTOCOL, HEADER_INSTALLATION].each do |h|
      val = headers[h] || headers[h.downcase]
      out[h.downcase] = val.to_s if val
      out[h] = val.to_s if val
    end
  end
  out
end

.secure_compare(a, b) ⇒ Object



52
53
54
55
56
57
58
59
# File 'lib/open_wire/webhook.rb', line 52

def secure_compare(a, b)
  return false if a.bytesize != b.bytesize

  l = a.unpack("C*")
  r = 0
  b.each_byte.with_index { |byte, i| r |= byte ^ l[i] }
  r.zero?
end

.verify_and_parse!(opts) ⇒ Object

opts:

secret: expected webhook secret (required unless allow_missing_secret)
body: raw request body string OR already-parsed Hash
headers: Hash-like (Rack env or ActionDispatch headers)

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/open_wire/webhook.rb', line 16

def verify_and_parse!(opts)
  secret = opts.fetch(:secret)
  body = opts.fetch(:body)
  headers = normalize_headers(opts[:headers] || {})

  provided = headers[HEADER_SECRET.downcase] || headers[HEADER_SECRET] || ""
  raise AuthError, "Missing X-Open-Wire-Secret" if provided.to_s.empty?
  raise AuthError, "Invalid Open-Wire webhook secret" unless secure_compare(provided.to_s, secret.to_s)

  protocol = headers[HEADER_PROTOCOL.downcase] || headers[HEADER_PROTOCOL]
  if protocol && protocol.to_s != OpenWire::PROTOCOL
    raise WebhookError, "Unsupported protocol #{protocol}"
  end

  Envelope.parse_inbound(body)
end