action_mailbox_amazon_ingress 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +18 -0
- data/.rubocop.yml +4 -0
- data/.ruby-version +1 -1
- data/Gemfile +9 -0
- data/README.md +14 -7
- data/Rakefile +69 -0
- data/action_mailbox_amazon_ingress.gemspec +5 -15
- data/app/controllers/action_mailbox/ingresses/amazon/inbound_emails_controller.rb +11 -64
- data/lib/action_mailbox_amazon_ingress/rspec/email.rb +10 -5
- data/lib/action_mailbox_amazon_ingress/rspec.rb +10 -4
- data/lib/action_mailbox_amazon_ingress/sns_notification.rb +98 -0
- data/lib/action_mailbox_amazon_ingress/version.rb +1 -1
- metadata +19 -115
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 94d231867bda21e8d23375b26f34761b3b0125d6bad53e312cb37be1fc939788
|
|
4
|
+
data.tar.gz: 912a90256bf244ef73a41d8e3b19b2e2d052e5fd52a9dcfaff3c8fb9e8c21a85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e1cbfa0915941aefa5d14431eba979447c9dfb7a306b61aaa362dc469c7842cac91e1b1f30881a9982f5628d124e7ee2ac959646fc71c2c005e7bc317adab464
|
|
7
|
+
data.tar.gz: 0c1d0f82bf9c430be873a343a27b88999db24211cc5e25909956ad84ace4c374bea4dadcf4f515b8fc8d18bd21b8dc0caf0c73129b39acb39333fc8ede6a4284
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: 'CI'
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ "master" ]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [ "master" ]
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout code
|
|
12
|
+
uses: actions/checkout@v4
|
|
13
|
+
- name: Install Ruby and gems
|
|
14
|
+
uses: ruby/setup-ruby@v1
|
|
15
|
+
with:
|
|
16
|
+
bundler-cache: true
|
|
17
|
+
- name: Run tests
|
|
18
|
+
run: make
|
data/.rubocop.yml
CHANGED
|
@@ -12,6 +12,7 @@ Metrics/BlockLength:
|
|
|
12
12
|
|
|
13
13
|
AllCops:
|
|
14
14
|
NewCops: enable
|
|
15
|
+
SuggestExtensions: false
|
|
15
16
|
Exclude:
|
|
16
17
|
- 'bin/**/*'
|
|
17
18
|
- 'db/migrate/**/*.rb'
|
|
@@ -32,3 +33,6 @@ Style/HashTransformKeys:
|
|
|
32
33
|
Enabled: true
|
|
33
34
|
Style/HashTransformValues:
|
|
34
35
|
Enabled: true
|
|
36
|
+
|
|
37
|
+
Style/HashSyntax:
|
|
38
|
+
Enabled: false
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
3.2.3
|
data/Gemfile
CHANGED
|
@@ -3,3 +3,12 @@
|
|
|
3
3
|
source 'https://rubygems.org'
|
|
4
4
|
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
5
5
|
gemspec
|
|
6
|
+
|
|
7
|
+
gem 'devpack', '~> 0.3.3'
|
|
8
|
+
gem 'rake', '~> 13.0'
|
|
9
|
+
gem 'rspec-its', '~> 1.3'
|
|
10
|
+
gem 'rspec-rails', '~> 6.1'
|
|
11
|
+
gem 'rubocop', '~> 1.63'
|
|
12
|
+
gem 'sqlite3', '~> 1.4'
|
|
13
|
+
gem 'strong_versions', '~> 0.4.5'
|
|
14
|
+
gem 'webmock', '~> 3.8'
|
data/README.md
CHANGED
|
@@ -14,22 +14,20 @@ gem 'action_mailbox_amazon_ingress', '~> 0.1.3'
|
|
|
14
14
|
|
|
15
15
|
### Amazon SES/SNS
|
|
16
16
|
|
|
17
|
-
Configure
|
|
17
|
+
1. [Configure SES](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications.html) to (save emails to S3)(https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-s3.html) or to send them as raw messages.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
https://example.com/rails/action_mailbox/amazon/inbound_emails
|
|
19
|
+
2. [Configure the SNS topic for SES or for the S3 action](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-sns.html) to send notifications to +/rails/action_mailbox/amazon/inbound_emails+. For example, if your website is hosted at https://www.example.com then configure _SNS_ to publish the _SES_ notification topic to this _HTTP_ endpoint: https://example.com/rails/action_mailbox/amazon/inbound_emails
|
|
22
20
|
|
|
23
21
|
### Rails
|
|
24
22
|
|
|
25
|
-
Configure _ActionMailbox_ to accept emails from Amazon SES:
|
|
23
|
+
1. Configure _ActionMailbox_ to accept emails from Amazon SES:
|
|
26
24
|
|
|
27
25
|
```
|
|
28
26
|
# config/environments/production.rb
|
|
29
27
|
config.action_mailbox.ingress = :amazon
|
|
30
28
|
```
|
|
31
29
|
|
|
32
|
-
Configure which _SNS_ topics will be accepted:
|
|
30
|
+
2. Configure which _SNS_ topics will be accepted:
|
|
33
31
|
|
|
34
32
|
```
|
|
35
33
|
# config/environments/production.rb
|
|
@@ -39,7 +37,7 @@ config.action_mailbox.amazon.subscribed_topics = %w(
|
|
|
39
37
|
)
|
|
40
38
|
```
|
|
41
39
|
|
|
42
|
-
Subscriptions will now be auto-confirmed and messages will be
|
|
40
|
+
SNS Subscriptions will now be auto-confirmed and messages will be automatically handled via _ActionMailbox_.
|
|
43
41
|
|
|
44
42
|
Note that even if you manually confirm subscriptions you will still need to provide a list of subscribed topics; messages from unrecognized topics will be ignored.
|
|
45
43
|
|
|
@@ -99,11 +97,20 @@ You may also pass the following keyword arguments to both helpers:
|
|
|
99
97
|
|
|
100
98
|
## Development
|
|
101
99
|
|
|
100
|
+
### Setup
|
|
101
|
+
|
|
102
|
+
`bin/setup`
|
|
103
|
+
|
|
104
|
+
### Testing
|
|
105
|
+
|
|
102
106
|
Ensure _Rubocop_, _RSpec_, and _StrongVersions_ compliance by running `make`:
|
|
103
107
|
|
|
104
108
|
```
|
|
105
109
|
make
|
|
106
110
|
```
|
|
111
|
+
### Updating AWS Fixtures
|
|
112
|
+
|
|
113
|
+
`bundle exec rake sign_aws_fixtures`
|
|
107
114
|
|
|
108
115
|
## Contributing
|
|
109
116
|
|
data/Rakefile
CHANGED
|
@@ -6,3 +6,72 @@ require 'rspec/core/rake_task'
|
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
8
|
task default: :spec
|
|
9
|
+
|
|
10
|
+
PRIVATE_KEY = 'spec/fixtures/pem/privatekey.pem'
|
|
11
|
+
CERTIFICATE = 'spec/fixtures/pem/certificate.pem'
|
|
12
|
+
AWS_FIXTURES = FileList['spec/fixtures/json/*.json'].exclude('**/*/invalid_signature.json')
|
|
13
|
+
SIGNABLE_KEYS = %w[
|
|
14
|
+
Message
|
|
15
|
+
MessageId
|
|
16
|
+
Subject
|
|
17
|
+
SubscribeURL
|
|
18
|
+
Timestamp
|
|
19
|
+
Token
|
|
20
|
+
TopicArn
|
|
21
|
+
Type
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
file PRIVATE_KEY do |t|
|
|
25
|
+
require 'openssl'
|
|
26
|
+
key = OpenSSL::PKey::RSA.new 2048
|
|
27
|
+
File.write(t.name, key.to_pem)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
file CERTIFICATE => PRIVATE_KEY do |t|
|
|
31
|
+
require 'openssl'
|
|
32
|
+
key = OpenSSL::PKey::RSA.new File.read(PRIVATE_KEY)
|
|
33
|
+
cert = OpenSSL::X509::Certificate.new
|
|
34
|
+
cert.version = 2
|
|
35
|
+
cert.serial = 2
|
|
36
|
+
cert.subject = OpenSSL::X509::Name.parse '/DC=org/DC=ruby-lang/CN=Ruby certificate'
|
|
37
|
+
cert.issuer = cert.subject # root CA is the issuer
|
|
38
|
+
cert.public_key = key.public_key
|
|
39
|
+
cert.not_before = Time.now
|
|
40
|
+
cert.not_after = cert.not_before + (1 * 365 * 24 * 60 * 60) # 10 years validity
|
|
41
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
|
42
|
+
ef.subject_certificate = cert
|
|
43
|
+
ef.issuer_certificate = cert
|
|
44
|
+
cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature', true))
|
|
45
|
+
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
|
|
46
|
+
cert.sign(key, OpenSSL::Digest.new('SHA256'))
|
|
47
|
+
|
|
48
|
+
File.write(t.name, cert.to_pem)
|
|
49
|
+
end
|
|
50
|
+
task certificates: [PRIVATE_KEY, CERTIFICATE]
|
|
51
|
+
|
|
52
|
+
desc 'Sign AWS SES fixtures, must be called if fixtures are modified.'
|
|
53
|
+
task sign_aws_fixtures: :certificates do
|
|
54
|
+
require 'openssl'
|
|
55
|
+
require 'json'
|
|
56
|
+
require 'base64'
|
|
57
|
+
|
|
58
|
+
key = OpenSSL::PKey::RSA.new File.read(PRIVATE_KEY)
|
|
59
|
+
|
|
60
|
+
AWS_FIXTURES.each do |fixture|
|
|
61
|
+
data = JSON.parse File.read(fixture)
|
|
62
|
+
string = canonical_string(data)
|
|
63
|
+
signed_string = key.sign('SHA1', string)
|
|
64
|
+
data['Signature'] = Base64.encode64(signed_string)
|
|
65
|
+
File.write(fixture, JSON.pretty_generate(data))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def canonical_string(message)
|
|
70
|
+
parts = []
|
|
71
|
+
|
|
72
|
+
SIGNABLE_KEYS.each do |key|
|
|
73
|
+
value = message[key]
|
|
74
|
+
parts << "#{key}\n#{value}\n" unless value.nil? || value.empty?
|
|
75
|
+
end
|
|
76
|
+
parts.join
|
|
77
|
+
end
|
|
@@ -9,31 +9,21 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.version = ActionMailboxAmazonIngress::VERSION
|
|
10
10
|
spec.authors = ['Bob Farrell']
|
|
11
11
|
spec.email = ['git@bob.frl']
|
|
12
|
-
spec.required_ruby_version = '>= 2
|
|
12
|
+
spec.required_ruby_version = '>= 3.2'
|
|
13
13
|
|
|
14
14
|
spec.summary = 'Amazon SES ingress for Rails ActionMailbox'
|
|
15
15
|
spec.description = 'Integrate Amazon SES with ActionMailbox'
|
|
16
16
|
spec.homepage = 'https://github.com/bobf/action_mailbox_amazon_ingress'
|
|
17
17
|
spec.license = 'MIT'
|
|
18
|
-
|
|
18
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
|
19
19
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
20
20
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
21
21
|
f.match(%r{^(test|spec|features)/})
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
|
-
spec.bindir = 'bin'
|
|
25
|
-
spec.executables = []
|
|
26
24
|
spec.require_paths = ['lib']
|
|
27
25
|
|
|
28
|
-
spec.add_runtime_dependency 'aws-sdk-
|
|
29
|
-
spec.
|
|
30
|
-
|
|
31
|
-
spec.add_development_dependency 'devpack', '~> 0.3.3'
|
|
32
|
-
spec.add_development_dependency 'rake', '~> 13.0'
|
|
33
|
-
spec.add_development_dependency 'rspec-its', '~> 1.3'
|
|
34
|
-
spec.add_development_dependency 'rspec-rails', '~> 4.0'
|
|
35
|
-
spec.add_development_dependency 'rubocop', '~> 0.90.0'
|
|
36
|
-
spec.add_development_dependency 'sqlite3', '~> 1.4'
|
|
37
|
-
spec.add_development_dependency 'strong_versions', '~> 0.4.5'
|
|
38
|
-
spec.add_development_dependency 'webmock', '~> 3.8'
|
|
26
|
+
spec.add_runtime_dependency 'aws-sdk-s3', '~> 1.151'
|
|
27
|
+
spec.add_runtime_dependency 'aws-sdk-sns', '~> 1.75'
|
|
28
|
+
spec.add_dependency 'actionmailbox', '~> 7.1'
|
|
39
29
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'action_mailbox_amazon_ingress/sns_notification'
|
|
4
|
+
|
|
3
5
|
module ActionMailbox
|
|
4
6
|
# Ingests inbound emails from Amazon SES/SNS and confirms subscriptions.
|
|
5
7
|
#
|
|
@@ -26,41 +28,16 @@ module ActionMailbox
|
|
|
26
28
|
# - <tt>401 Unauthorized</tt> if a request does not contain a valid signature
|
|
27
29
|
# - <tt>404 Not Found</tt> if the Amazon ingress has not been configured
|
|
28
30
|
# - <tt>422 Unprocessable Entity</tt> if a request provides invalid parameters
|
|
29
|
-
#
|
|
30
|
-
# == Usage
|
|
31
|
-
#
|
|
32
|
-
# 1. Tell Action Mailbox to accept emails from Amazon SES:
|
|
33
|
-
#
|
|
34
|
-
# # config/environments/production.rb
|
|
35
|
-
# config.action_mailbox.ingress = :amazon
|
|
36
|
-
#
|
|
37
|
-
# 2. Configure which SNS topics will be accepted:
|
|
38
|
-
#
|
|
39
|
-
# config.action_mailbox.amazon.subscribed_topics = %w(
|
|
40
|
-
# arn:aws:sns:eu-west-1:123456789001:example-topic-1
|
|
41
|
-
# arn:aws:sns:us-east-1:123456789002:example-topic-2
|
|
42
|
-
# )
|
|
43
|
-
#
|
|
44
|
-
# 3. {Configure SES}[https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications.html]
|
|
45
|
-
# to route emails through SNS.
|
|
46
|
-
#
|
|
47
|
-
# Configure SNS to send emails to +/rails/action_mailbox/amazon/inbound_emails+.
|
|
48
|
-
#
|
|
49
|
-
# If your application is found at <tt>https://example.com</tt> you would
|
|
50
|
-
# specify the fully-qualified URL <tt>https://example.com/rails/action_mailbox/amazon/inbound_emails</tt>.
|
|
51
|
-
#
|
|
52
31
|
|
|
53
32
|
module Ingresses
|
|
54
33
|
module Amazon
|
|
55
34
|
class InboundEmailsController < ActionMailbox::BaseController
|
|
56
|
-
before_action :verify_authenticity
|
|
57
|
-
before_action :validate_topic
|
|
58
|
-
before_action :confirm_subscription
|
|
35
|
+
before_action :verify_authenticity, :validate_topic, :confirm_subscription
|
|
59
36
|
|
|
60
37
|
def create
|
|
61
|
-
head :bad_request unless
|
|
38
|
+
head :bad_request unless notification.message_content.present?
|
|
62
39
|
|
|
63
|
-
ActionMailbox::InboundEmail.create_and_extract_message_id!(
|
|
40
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id!(notification.message_content)
|
|
64
41
|
head :no_content
|
|
65
42
|
end
|
|
66
43
|
|
|
@@ -68,60 +45,30 @@ module ActionMailbox
|
|
|
68
45
|
|
|
69
46
|
def verify_authenticity
|
|
70
47
|
head :bad_request unless notification.present?
|
|
71
|
-
head :unauthorized unless verified?
|
|
48
|
+
head :unauthorized unless notification.verified?
|
|
72
49
|
end
|
|
73
50
|
|
|
74
51
|
def confirm_subscription
|
|
75
|
-
return unless notification
|
|
76
|
-
return head :ok if
|
|
52
|
+
return unless notification.type == 'SubscriptionConfirmation'
|
|
53
|
+
return head :ok if notification.subscription_confirmed?
|
|
77
54
|
|
|
78
55
|
Rails.logger.error('SNS subscription confirmation request rejected.')
|
|
79
56
|
head :unprocessable_entity
|
|
80
57
|
end
|
|
81
58
|
|
|
82
59
|
def validate_topic
|
|
83
|
-
return if valid_topics.include?(topic)
|
|
60
|
+
return if valid_topics.include?(notification.topic)
|
|
84
61
|
|
|
85
62
|
Rails.logger.warn("Ignoring unknown topic: #{topic}")
|
|
86
63
|
head :unauthorized
|
|
87
64
|
end
|
|
88
65
|
|
|
89
|
-
def confirmation_response_code
|
|
90
|
-
@confirmation_response_code ||= begin
|
|
91
|
-
Net::HTTP.get_response(URI(notification['SubscribeURL'])).code
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
66
|
def notification
|
|
96
|
-
@notification ||=
|
|
97
|
-
rescue JSON::ParserError => e
|
|
98
|
-
Rails.logger.warn("Unable to parse SNS notification: #{e}")
|
|
99
|
-
nil
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def verified?
|
|
103
|
-
verifier.authentic?(@notification.to_json)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def verifier
|
|
107
|
-
Aws::SNS::MessageVerifier.new
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def message
|
|
111
|
-
@message ||= JSON.parse(notification['Message'])
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def mail
|
|
115
|
-
return nil unless notification['Type'] == 'Notification'
|
|
116
|
-
return nil unless message['notificationType'] == 'Received'
|
|
117
|
-
|
|
118
|
-
message['content']
|
|
67
|
+
@notification ||= ActionMailboxAmazonIngress::SnsNotification.new(request.raw_post)
|
|
119
68
|
end
|
|
120
69
|
|
|
121
70
|
def topic
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
notification['TopicArn']
|
|
71
|
+
@topic ||= notification.topic
|
|
125
72
|
end
|
|
126
73
|
|
|
127
74
|
def valid_topics
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
module ActionMailboxAmazonIngress
|
|
4
4
|
module RSpec
|
|
5
5
|
class Email
|
|
6
|
-
def initialize(authentic: true, topic: 'topic:arn:default', mail: default_mail)
|
|
6
|
+
def initialize(authentic: true, topic: 'topic:arn:default', mail: default_mail, message_params: {})
|
|
7
7
|
@authentic = authentic
|
|
8
8
|
@topic = topic
|
|
9
9
|
@mail = mail
|
|
10
|
+
@message_params = message_params
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def headers
|
|
@@ -21,13 +22,17 @@ module ActionMailboxAmazonIngress
|
|
|
21
22
|
{
|
|
22
23
|
'Type' => 'Notification',
|
|
23
24
|
'TopicArn' => @topic,
|
|
24
|
-
'Message' =>
|
|
25
|
-
'notificationType' => 'Received',
|
|
26
|
-
'content' => @mail.encoded
|
|
27
|
-
}.to_json
|
|
25
|
+
'Message' => message_json
|
|
28
26
|
}
|
|
29
27
|
end
|
|
30
28
|
|
|
29
|
+
def message_json
|
|
30
|
+
{
|
|
31
|
+
'notificationType' => 'Received',
|
|
32
|
+
'content' => @mail.encoded
|
|
33
|
+
}.merge(@message_params).to_json
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
def authentic?
|
|
32
37
|
@authentic
|
|
33
38
|
end
|
|
@@ -9,15 +9,21 @@ module ActionMailboxAmazonIngress
|
|
|
9
9
|
subscription_confirmation = SubscriptionConfirmation.new(**options)
|
|
10
10
|
stub_aws_sns_message_verifier(subscription_confirmation)
|
|
11
11
|
stub_aws_sns_subscription_request
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
post subscription_confirmation.url,
|
|
14
|
+
params: subscription_confirmation.params,
|
|
15
|
+
headers: subscription_confirmation.headers,
|
|
16
|
+
as: :json
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def amazon_ingress_deliver_email(options = {})
|
|
17
20
|
email = Email.new(**options)
|
|
18
21
|
stub_aws_sns_message_verifier(email)
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
post email.url,
|
|
24
|
+
params: email.params,
|
|
25
|
+
headers: email.headers,
|
|
26
|
+
as: :json
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
private
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aws-sdk-sns'
|
|
4
|
+
|
|
5
|
+
module ActionMailboxAmazonIngress
|
|
6
|
+
class SnsNotification
|
|
7
|
+
class MessageContentError < StandardError; end
|
|
8
|
+
|
|
9
|
+
def initialize(request_body)
|
|
10
|
+
@request_body = request_body
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def subscription_confirmed?
|
|
14
|
+
confirmation_response.code&.start_with?('2')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def verified?
|
|
18
|
+
Aws::SNS::MessageVerifier.new.authentic?(@request_body)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def topic
|
|
22
|
+
notification.fetch(:TopicArn)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def type
|
|
26
|
+
notification.fetch(:Type)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def message_content
|
|
30
|
+
raise MessageContentError, 'Incoming emails must have notificationType `Received`' unless receipt?
|
|
31
|
+
|
|
32
|
+
if content_in_s3?
|
|
33
|
+
s3_content
|
|
34
|
+
else
|
|
35
|
+
return message[:content] unless destination
|
|
36
|
+
|
|
37
|
+
"X-Original-To: #{destination}\n#{message[:content]}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def notification
|
|
44
|
+
@notification ||= JSON.parse(@request_body, symbolize_names: true)
|
|
45
|
+
rescue JSON::ParserError => e
|
|
46
|
+
Rails.logger.warn("Unable to parse SNS notification: #{e}")
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def s3_content
|
|
51
|
+
require 'aws-sdk-s3'
|
|
52
|
+
|
|
53
|
+
Aws::S3::Client
|
|
54
|
+
.new(region: region)
|
|
55
|
+
.get_object(key: key, bucket: bucket)
|
|
56
|
+
.body
|
|
57
|
+
.string
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def message
|
|
61
|
+
@message ||= JSON.parse(notification[:Message], symbolize_names: true)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def destination
|
|
65
|
+
message.dig(:mail, :destination)&.first
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def action
|
|
69
|
+
return unless message[:receipt]
|
|
70
|
+
|
|
71
|
+
message.fetch(:receipt).fetch(:action)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def bucket
|
|
75
|
+
action.fetch(:bucketName)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def region
|
|
79
|
+
action.fetch(:topicArn).split(':')[3]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def key
|
|
83
|
+
action.fetch(:objectKey)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def content_in_s3?
|
|
87
|
+
action&.fetch(:type) == 'S3'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def receipt?
|
|
91
|
+
message.fetch(:notificationType) == 'Received'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def confirmation_response
|
|
95
|
+
@confirmation_response ||= Net::HTTP.get_response(URI(notification[:SubscribeURL]))
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
metadata
CHANGED
|
@@ -1,155 +1,57 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: action_mailbox_amazon_ingress
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bob Farrell
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-06-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: aws-sdk-
|
|
14
|
+
name: aws-sdk-s3
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.
|
|
19
|
+
version: '1.151'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: rails
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '6.1'
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '6.1'
|
|
26
|
+
version: '1.151'
|
|
41
27
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.3.3
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.3.3
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rake
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '13.0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '13.0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: rspec-its
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '1.3'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '1.3'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rspec-rails
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '4.0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '4.0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rubocop
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - "~>"
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: 0.90.0
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - "~>"
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: 0.90.0
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: sqlite3
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - "~>"
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '1.4'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - "~>"
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '1.4'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: strong_versions
|
|
28
|
+
name: aws-sdk-sns
|
|
127
29
|
requirement: !ruby/object:Gem::Requirement
|
|
128
30
|
requirements:
|
|
129
31
|
- - "~>"
|
|
130
32
|
- !ruby/object:Gem::Version
|
|
131
|
-
version:
|
|
132
|
-
type: :
|
|
33
|
+
version: '1.75'
|
|
34
|
+
type: :runtime
|
|
133
35
|
prerelease: false
|
|
134
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
37
|
requirements:
|
|
136
38
|
- - "~>"
|
|
137
39
|
- !ruby/object:Gem::Version
|
|
138
|
-
version:
|
|
40
|
+
version: '1.75'
|
|
139
41
|
- !ruby/object:Gem::Dependency
|
|
140
|
-
name:
|
|
42
|
+
name: actionmailbox
|
|
141
43
|
requirement: !ruby/object:Gem::Requirement
|
|
142
44
|
requirements:
|
|
143
45
|
- - "~>"
|
|
144
46
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '
|
|
146
|
-
type: :
|
|
47
|
+
version: '7.1'
|
|
48
|
+
type: :runtime
|
|
147
49
|
prerelease: false
|
|
148
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
51
|
requirements:
|
|
150
52
|
- - "~>"
|
|
151
53
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '
|
|
54
|
+
version: '7.1'
|
|
153
55
|
description: Integrate Amazon SES with ActionMailbox
|
|
154
56
|
email:
|
|
155
57
|
- git@bob.frl
|
|
@@ -157,6 +59,7 @@ executables: []
|
|
|
157
59
|
extensions: []
|
|
158
60
|
extra_rdoc_files: []
|
|
159
61
|
files:
|
|
62
|
+
- ".github/workflows/ruby.yml"
|
|
160
63
|
- ".gitignore"
|
|
161
64
|
- ".rspec"
|
|
162
65
|
- ".rubocop.yml"
|
|
@@ -181,11 +84,13 @@ files:
|
|
|
181
84
|
- lib/action_mailbox_amazon_ingress/rspec.rb
|
|
182
85
|
- lib/action_mailbox_amazon_ingress/rspec/email.rb
|
|
183
86
|
- lib/action_mailbox_amazon_ingress/rspec/subscription_confirmation.rb
|
|
87
|
+
- lib/action_mailbox_amazon_ingress/sns_notification.rb
|
|
184
88
|
- lib/action_mailbox_amazon_ingress/version.rb
|
|
185
89
|
homepage: https://github.com/bobf/action_mailbox_amazon_ingress
|
|
186
90
|
licenses:
|
|
187
91
|
- MIT
|
|
188
|
-
metadata:
|
|
92
|
+
metadata:
|
|
93
|
+
rubygems_mfa_required: 'true'
|
|
189
94
|
post_install_message:
|
|
190
95
|
rdoc_options: []
|
|
191
96
|
require_paths:
|
|
@@ -194,15 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
194
99
|
requirements:
|
|
195
100
|
- - ">="
|
|
196
101
|
- !ruby/object:Gem::Version
|
|
197
|
-
version: '2
|
|
102
|
+
version: '3.2'
|
|
198
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
104
|
requirements:
|
|
200
105
|
- - ">="
|
|
201
106
|
- !ruby/object:Gem::Version
|
|
202
107
|
version: '0'
|
|
203
108
|
requirements: []
|
|
204
|
-
|
|
205
|
-
rubygems_version: 2.7.6
|
|
109
|
+
rubygems_version: 3.4.19
|
|
206
110
|
signing_key:
|
|
207
111
|
specification_version: 4
|
|
208
112
|
summary: Amazon SES ingress for Rails ActionMailbox
|