Class: Heroic::SNS::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/heroic/sns/message.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(json) ⇒ Message

Returns a new instance of Message.



32
33
34
35
36
# 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

Instance Method Details

#==(other) ⇒ Object



99
100
101
# File 'lib/heroic/sns/message.rb', line 99

def ==(other)
  other.is_a?(Message) && @msg == other.instance_variable_get(:@msg)
end

#bodyObject

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.



81
82
83
# File 'lib/heroic/sns/message.rb', line 81

def body
  @msg['Message']
end

#hashObject



103
104
105
# File 'lib/heroic/sns/message.rb', line 103

def hash
  @msg.hash
end

#idObject

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.



51
52
53
# File 'lib/heroic/sns/message.rb', line 51

def id
  @msg['MessageId']
end

#signatureObject

The message signature data, Base-64 decoded.



69
70
71
# File 'lib/heroic/sns/message.rb', line 69

def signature
  Base64::decode64(@msg['Signature'])
end

#signature_versionObject



60
61
62
# File 'lib/heroic/sns/message.rb', line 60

def signature_version
  @msg['SignatureVersion']
end

#signing_cert_urlObject



64
65
66
# File 'lib/heroic/sns/message.rb', line 64

def signing_cert_url
  @msg['SigningCertURL']
end

#subjectObject

The message may not have a subject.



74
75
76
# File 'lib/heroic/sns/message.rb', line 74

def subject
  @msg['Subject']
end

#subscribe_urlObject



85
86
87
# File 'lib/heroic/sns/message.rb', line 85

def subscribe_url
  @msg['SubscribeURL']
end

#timestampObject

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



56
57
58
# File 'lib/heroic/sns/message.rb', line 56

def timestamp
  Time.xmlschema(@msg['Timestamp'])
end

#to_jsonObject

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



117
118
119
# File 'lib/heroic/sns/message.rb', line 117

def to_json
  @msg.to_json
end

#to_sObject



107
108
109
110
111
112
113
# 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

#tokenObject

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



95
96
97
# File 'lib/heroic/sns/message.rb', line 95

def token
  @msg['Token']
end

#topic_arnObject



44
45
46
# File 'lib/heroic/sns/message.rb', line 44

def topic_arn
  @msg['TopicArn']
end

#typeObject

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



40
41
42
# File 'lib/heroic/sns/message.rb', line 40

def type
  @msg['Type']
end

#unsubscribe_urlObject



89
90
91
# File 'lib/heroic/sns/message.rb', line 89

def unsubscribe_url
  @msg['UnsubscribeURL']
end

#verify!Object

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

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

Raises:



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 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