sns_endpoint 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +59 -0
- data/Rakefile +2 -0
- data/lib/message.rb +159 -0
- data/lib/sns_endpoint.rb +36 -0
- data/lib/sns_endpoint/version.rb +3 -0
- data/sns_endpoint.gemspec +22 -0
- metadata +114 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Piotr Szmielew
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# SnsEndpoint
|
2
|
+
|
3
|
+
Simple gem containing Sinatra engine, designed to help you utilize SNS http post service.
|
4
|
+
Use inside Rails (as an engine) or outside - as a standalone application.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'sns_endpoint'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install sns_endpoint
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Configure using SnsEndpoint.setup, providing block, like this:
|
23
|
+
```ruby
|
24
|
+
SnsEndpoint.setup do |config|
|
25
|
+
config.topics_list = ['first_topic', 'second_topic'] #list of topics that endpoint should respond to subscription request
|
26
|
+
config.subscribe_proc = Proc.new { |message| p message } #proc that should be executed when subscribe request got received and responded to, passed argument is message (json object)
|
27
|
+
config.message_proc = Proc.new { |message| p message } #proc that should be executed when message got send to endpoint
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
for example in Rails initializer.
|
32
|
+
|
33
|
+
Use as Rails engine:
|
34
|
+
|
35
|
+
* mount in routes.rb:
|
36
|
+
```ruby
|
37
|
+
mount SnsEndpoint::Core => "/sns_endpoint"
|
38
|
+
```
|
39
|
+
|
40
|
+
Use as a standalone app:
|
41
|
+
Example script:
|
42
|
+
```ruby
|
43
|
+
require 'rubygems'
|
44
|
+
require 'sns_endpoint'
|
45
|
+
SnsEndpoint.setup do |config|
|
46
|
+
config.topics_list = ['first_topic', 'second_topic'] #list of topics that endpoint should respond to subscription request
|
47
|
+
config.subscribe_proc = Proc.new { |message| p message } #proc that should be executed when subscribe request got received and responded to, passed argument is message (json object)
|
48
|
+
config.message_proc = Proc.new { |message| p message } #proc that should be executed when message got send to endpoint
|
49
|
+
end
|
50
|
+
SnsEndpoint::Core.run!
|
51
|
+
```
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/message.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
5
|
+
# the License is located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
12
|
+
# language governing permissions and limitations under the License.
|
13
|
+
|
14
|
+
|
15
|
+
#modyfied version of message.rb from Aws-sdk fork by petemounce
|
16
|
+
|
17
|
+
require 'base64'
|
18
|
+
require 'json'
|
19
|
+
require 'openssl'
|
20
|
+
require 'httparty'
|
21
|
+
|
22
|
+
module SnsEndpoint
|
23
|
+
module AWS
|
24
|
+
class SNS
|
25
|
+
class MessageWasNotAuthenticError < StandardError
|
26
|
+
end
|
27
|
+
class Message
|
28
|
+
attr_accessor :origin, :raw
|
29
|
+
|
30
|
+
def initialize sns
|
31
|
+
if sns.is_a? String
|
32
|
+
@raw = parse_from sns
|
33
|
+
else
|
34
|
+
@raw = sns
|
35
|
+
end
|
36
|
+
@origin = :sns
|
37
|
+
end
|
38
|
+
|
39
|
+
def [] key
|
40
|
+
@raw[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
def authentic?
|
44
|
+
begin
|
45
|
+
decoded_from_base64 = decode signature
|
46
|
+
public_key = get_public_key_from signing_cert_url
|
47
|
+
public_key.verify OpenSSL::Digest::SHA1.new, decoded_from_base64, canonical_string
|
48
|
+
rescue MessageWasNotAuthenticError
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def type
|
54
|
+
case when @raw['Type'] =~ /SubscriptionConfirmation/i
|
55
|
+
then :SubscriptionConfirmation
|
56
|
+
when @raw['Type'] =~ /Notification/i
|
57
|
+
then :Notification
|
58
|
+
when @raw['Type'] =~ /UnsubscribeConfirmation/i
|
59
|
+
then :UnsubscribeConfirmation
|
60
|
+
else
|
61
|
+
:unknown
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def message_id
|
66
|
+
@raw['MessageId']
|
67
|
+
end
|
68
|
+
|
69
|
+
def topic_arn
|
70
|
+
@raw['TopicArn']
|
71
|
+
end
|
72
|
+
|
73
|
+
def subject
|
74
|
+
@raw['Subject']
|
75
|
+
end
|
76
|
+
|
77
|
+
def message
|
78
|
+
@raw['Message']
|
79
|
+
end
|
80
|
+
|
81
|
+
def timestamp
|
82
|
+
@raw['Timestamp']
|
83
|
+
end
|
84
|
+
|
85
|
+
def signature
|
86
|
+
@raw['Signature']
|
87
|
+
end
|
88
|
+
|
89
|
+
def signature_version
|
90
|
+
@raw['SignatureVersion']
|
91
|
+
end
|
92
|
+
|
93
|
+
def signing_cert_url
|
94
|
+
@raw['SigningCertURL']
|
95
|
+
end
|
96
|
+
|
97
|
+
def unsubscribe_url
|
98
|
+
@raw['UnsubscribeURL']
|
99
|
+
end
|
100
|
+
|
101
|
+
def subscribe_url
|
102
|
+
@raw['SubscribeURL']
|
103
|
+
end
|
104
|
+
|
105
|
+
def token
|
106
|
+
@raw['Token']
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_from json
|
110
|
+
JSON.parse json
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
def decode raw
|
115
|
+
Base64.decode64 raw
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_public_key_from(x509_pem_url)
|
119
|
+
cert_pem = download x509_pem_url
|
120
|
+
x509 = OpenSSL::X509::Certificate.new(cert_pem)
|
121
|
+
OpenSSL::PKey::RSA.new(x509.public_key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def canonical_string
|
125
|
+
if type == :SubscriptionConfirmation
|
126
|
+
text = "Message\n#{message}\n"
|
127
|
+
text += "MessageId\n#{message_id}\n"
|
128
|
+
text += "SubscribeURL\n#{subscribe_url}\n"
|
129
|
+
text += "Timestamp\n#{timestamp}\n"
|
130
|
+
text += "Token\n#{token}\n"
|
131
|
+
text += "TopicArn\n#{topic_arn}\n"
|
132
|
+
text += "Type\n#{type}\n"
|
133
|
+
else
|
134
|
+
text = "Message\n#{message}\n"
|
135
|
+
text += "MessageId\n#{message_id}\n"
|
136
|
+
text += "Subject\n#{subject}\n" unless subject.nil? or subject.empty?
|
137
|
+
text += "Timestamp\n#{timestamp}\n"
|
138
|
+
text += "TopicArn\n#{topic_arn}\n"
|
139
|
+
text += "Type\n#{type}\n"
|
140
|
+
end
|
141
|
+
text
|
142
|
+
end
|
143
|
+
|
144
|
+
def download url
|
145
|
+
raise MessageWasNotAuthenticError, "cert is not hosted at AWS URL (https): #{url}" unless url =~ /^https.*amazonaws\.com\/.*$/i
|
146
|
+
tries = 0
|
147
|
+
begin
|
148
|
+
response = HTTParty.get url
|
149
|
+
response.body
|
150
|
+
rescue => msg
|
151
|
+
tries += 1
|
152
|
+
retry if tries <= 3
|
153
|
+
raise StandardError, "SNS signing cert could not be retrieved after #{tries} tries.\n#{msg}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/sns_endpoint.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require 'bundler/setup'
|
3
|
+
require "sns_endpoint/version"
|
4
|
+
require 'sinatra'
|
5
|
+
require 'json'
|
6
|
+
require 'message'
|
7
|
+
|
8
|
+
module SnsEndpoint
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :topics_list, :subscribe_proc, :message_proc
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.setup(&block)
|
15
|
+
yield self
|
16
|
+
end
|
17
|
+
|
18
|
+
class Core < Sinatra::Base
|
19
|
+
|
20
|
+
post '/' do
|
21
|
+
json = JSON.parse(request.body.read)
|
22
|
+
sns = SnsEndpoint::AWS::SNS::Message.new json
|
23
|
+
if sns.authentic?
|
24
|
+
if sns.type == :SubscriptionConfirmation
|
25
|
+
if SnsEndpoint.topics_list.include? json['TopicArn']
|
26
|
+
HTTParty.get json['SubscribeURL']
|
27
|
+
SnsEndpoint.subscribe_proc.call(json)
|
28
|
+
end
|
29
|
+
elsif sns.type == :Notification
|
30
|
+
SnsEndpoint.message_proc.call(json)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/sns_endpoint/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Piotr Szmielew"]
|
6
|
+
gem.email = ["p.szmielew@ava.waw.pl"]
|
7
|
+
gem.description = %q{Gem containing sinatra engine to utilize SNS notifications.}
|
8
|
+
gem.summary = %q{Gem containing sinatra engine to utilize SNS notifications.}
|
9
|
+
gem.homepage = "http://github.com/elibri/sns_endpoint"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "sns_endpoint"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SnsEndpoint::VERSION
|
17
|
+
|
18
|
+
|
19
|
+
gem.add_runtime_dependency "httparty"
|
20
|
+
gem.add_runtime_dependency "sinatra"
|
21
|
+
gem.add_runtime_dependency "json"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sns_endpoint
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Piotr Szmielew
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-06-24 00:00:00 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: httparty
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sinatra
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :runtime
|
46
|
+
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: json
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
type: :runtime
|
60
|
+
version_requirements: *id003
|
61
|
+
description: Gem containing sinatra engine to utilize SNS notifications.
|
62
|
+
email:
|
63
|
+
- p.szmielew@ava.waw.pl
|
64
|
+
executables: []
|
65
|
+
|
66
|
+
extensions: []
|
67
|
+
|
68
|
+
extra_rdoc_files: []
|
69
|
+
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- Gemfile
|
73
|
+
- LICENSE
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- lib/message.rb
|
77
|
+
- lib/sns_endpoint.rb
|
78
|
+
- lib/sns_endpoint/version.rb
|
79
|
+
- sns_endpoint.gemspec
|
80
|
+
homepage: http://github.com/elibri/sns_endpoint
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.8.21
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Gem containing sinatra engine to utilize SNS notifications.
|
113
|
+
test_files: []
|
114
|
+
|