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.
Constants
- CANONICAL_KEYS_FOR_TYPE
- CANONICAL_NOTIFICATION_KEYS
- CANONICAL_SUBSCRIPTION_KEYS
Public Class Methods
# 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
# File lib/heroic/sns/message.rb, line 99 def ==(other) other.is_a?(Message) && @msg == other.instance_variable_get(:@msg) end
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
# File lib/heroic/sns/message.rb, line 103 def hash @msg.hash end
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
The message signature data, Base-64 decoded.
# File lib/heroic/sns/message.rb, line 69 def signature Base64::decode64(@msg['Signature']) end
# File lib/heroic/sns/message.rb, line 60 def signature_version @msg['SignatureVersion'] end
# File lib/heroic/sns/message.rb, line 64 def signing_cert_url @msg['SigningCertURL'] end
The message may not have a subject.
# File lib/heroic/sns/message.rb, line 74 def subject @msg['Subject'] end
# File lib/heroic/sns/message.rb, line 85 def subscribe_url @msg['SubscribeURL'] end
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
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
# 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
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
# File lib/heroic/sns/message.rb, line 44 def topic_arn @msg['TopicArn'] end
The message type will be one of SubscriptionConfirmation
, UnsubscribeConfirmation
, and Notification
.
# File lib/heroic/sns/message.rb, line 40 def type @msg['Type'] end
# File lib/heroic/sns/message.rb, line 89 def unsubscribe_url @msg['UnsubscribeURL'] end
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
# 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