class Heroic::SNS::Message

Encapsulates an SNS message. Since Endpoint takes care of authenticating the message, most of the time you will simply be interested in retrieving the subject and body from the message and acting on it.

See: docs.aws.amazon.com/sns/latest/gsg/json-formats.html

Constants

CANONICAL_KEYS_FOR_TYPE
CANONICAL_NOTIFICATION_KEYS
CANONICAL_SUBSCRIPTION_KEYS

Public Class Methods

new(json) click to toggle source
# File lib/heroic/sns/message.rb, line 32
def initialize(json)
  @msg = ::JSON.parse(json)
rescue JSON::ParserError => e
  raise Error.new("failed to parse message as JSON: #{e.message}")
end

Public Instance Methods

==(other) click to toggle source
# File lib/heroic/sns/message.rb, line 99
def ==(other)
  other.is_a?(Message) && @msg == other.instance_variable_get(:@msg)
end
body() click to toggle source

The message payload. As far as Amazon and this class are concerned, the message payload is just a string of bytes. If you are expecting, for example, a JSON object, you will need to pass this to a JSON parser.

# File lib/heroic/sns/message.rb, line 81
def body
  @msg['Message']
end
hash() click to toggle source
# File lib/heroic/sns/message.rb, line 103
def hash
  @msg.hash
end
id() click to toggle source

A Universally Unique Identifier, unique for each message published. For a notification that Amazon SNS resends during a retry, the message ID of the original message is used.

# File lib/heroic/sns/message.rb, line 51
def id
  @msg['MessageId']
end
signature() click to toggle source

The message signature data, Base-64 decoded.

# File lib/heroic/sns/message.rb, line 69
def signature
  Base64::decode64(@msg['Signature'])
end
signature_version() click to toggle source
# File lib/heroic/sns/message.rb, line 60
def signature_version
  @msg['SignatureVersion']
end
signing_cert_url() click to toggle source
# File lib/heroic/sns/message.rb, line 64
def signing_cert_url
  @msg['SigningCertURL']
end
subject() click to toggle source

The message may not have a subject.

# File lib/heroic/sns/message.rb, line 74
def subject
  @msg['Subject']
end
subscribe_url() click to toggle source
# File lib/heroic/sns/message.rb, line 85
def subscribe_url
  @msg['SubscribeURL']
end
timestamp() click to toggle source

The timestamp when the message was published, as a Time object.

# File lib/heroic/sns/message.rb, line 56
def timestamp
  Time.xmlschema(@msg['Timestamp'])
end
to_json() click to toggle source

Returns a JSON serialization of the message. Note that it may not be identical to the serialization that was retrieved from the network.

# File lib/heroic/sns/message.rb, line 117
def to_json
  @msg.to_json
end
to_s() click to toggle source
# File lib/heroic/sns/message.rb, line 107
def to_s
  string = "<SNSMessage:\n"
  @msg.each do |k,v|
    string << sprintf("  %s: %s\n", k, v.inspect)
  end
  string << ">"
end
token() click to toggle source

The token is used to confirm subscriptions via the SNS API. If you visit the subscribe_url, you can ignore this field.

# File lib/heroic/sns/message.rb, line 95
def token
  @msg['Token']
end
topic_arn() click to toggle source
# File lib/heroic/sns/message.rb, line 44
def topic_arn
  @msg['TopicArn']
end
type() click to toggle source

The message type will be one of SubscriptionConfirmation, UnsubscribeConfirmation, and Notification.

# File lib/heroic/sns/message.rb, line 40
def type
  @msg['Type']
end
unsubscribe_url() click to toggle source
# File lib/heroic/sns/message.rb, line 89
def unsubscribe_url
  @msg['UnsubscribeURL']
end
verify!() click to toggle source

Verifies the message signature. Raises Error if it is not valid.

See: docs.aws.amazon.com/sns/latest/gsg/SendMessageToHttp.verify.signature.html

# File lib/heroic/sns/message.rb, line 124
def verify!
  age = Time.now - timestamp
  raise Error.new("timestamp is in the future, age: #{age}", self) if age < 0
  raise Error.new("timestamp is too old", self) if age > MAXIMUM_ALLOWED_AGE
  if signature_version != '1'
    raise Error.new("unknown signature version: #{signature_version}", self)
  end
  if signing_cert_url !~ VALID_AWS_URL_PATTERN
    raise Error.new("signing certificate is not from amazonaws.com", self)
  end
  text = string_to_sign # will warn of invalid Type
  cert = CERTIFICATE_CACHE.get(signing_cert_url)
  digest = OpenSSL::Digest::SHA1.new
  unless cert.public_key.verify(digest, signature, text)
    raise Error.new("message signature is invalid", self)
  end
end

Private Instance Methods

string_to_sign() click to toggle source
# File lib/heroic/sns/message.rb, line 154
def string_to_sign
  keys = CANONICAL_KEYS_FOR_TYPE[self.type]
  raise Error.new("unrecognized message type: #{self.type}", self) unless keys
  string = String.new
  keys.each do |key|
    if @msg.has_key?(key) # in case message has no Subject
      string << key << "\n" << @msg[key] << "\n"
    end
  end
  return string
end