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.



29
30
31
32
33
# File 'lib/heroic/sns/message.rb', line 29

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



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

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.



78
79
80
# File 'lib/heroic/sns/message.rb', line 78

def body
  @msg['Message']
end

#hashObject



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

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.



48
49
50
# File 'lib/heroic/sns/message.rb', line 48

def id
  @msg['MessageId']
end

#signatureObject

The message signature data, Base-64 decoded.



66
67
68
# File 'lib/heroic/sns/message.rb', line 66

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

#signature_versionObject



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

def signature_version
  @msg['SignatureVersion']
end

#signing_cert_urlObject



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

def signing_cert_url
  @msg['SigningCertURL']
end

#subjectObject

The message may not have a subject.



71
72
73
# File 'lib/heroic/sns/message.rb', line 71

def subject
  @msg['Subject']
end

#subscribe_urlObject



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

def subscribe_url
  @msg['SubscribeURL']
end

#timestampObject

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



53
54
55
# File 'lib/heroic/sns/message.rb', line 53

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.



114
115
116
# File 'lib/heroic/sns/message.rb', line 114

def to_json
  @msg.to_json
end

#to_sObject



104
105
106
107
108
109
110
# File 'lib/heroic/sns/message.rb', line 104

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.



92
93
94
# File 'lib/heroic/sns/message.rb', line 92

def token
  @msg['Token']
end

#topic_arnObject



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

def topic_arn
  @msg['TopicArn']
end

#typeObject

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



37
38
39
# File 'lib/heroic/sns/message.rb', line 37

def type
  @msg['Type']
end

#unsubscribe_urlObject



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

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:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/heroic/sns/message.rb', line 121

def verify!
  age = Time.now - timestamp
  raise Error.new("timestamp is in the future", 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 !~ %r[^https://.*amazonaws\.com/]
    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