aws-ses 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d83b84b06b584bdb5c4643e75dde6cd185d7b54
4
- data.tar.gz: f7c7c7d108a5e87ba91b5249f2e8795a6ace7606
3
+ metadata.gz: 05f8041ea9198a602f2ce4882d6e97cc02975836
4
+ data.tar.gz: 751a555c98abe3962855fa0cc68e4048215e5c02
5
5
  SHA512:
6
- metadata.gz: bd17ce170e886f373038bbee96b593398ad2f9a1397bcce409cc7d9c0ecdcd6adceca4756d9380b7d114cc503797065de349df675ff409d1a8ab8806c147e00b
7
- data.tar.gz: 97d79a71412f8bf55659ac2bca9697072b5ad0a7ec6531d45ab249d56e817f6b56f0f432b1a2f21acdf9a5cb3c68869dd3aa5f50ae853828b6cc9bd25a46fd47
6
+ metadata.gz: 2d42dc94c62bf6379c1f8bb4ebeb62203270a1b786bbed459138b6cb1912260822621f4ec753d3ad1c7c244a0d56e2b730f289c30a5a133dc41b34a5be75d2de
7
+ data.tar.gz: 68b570b0043c013cd72858d0136840357b99f5110eacec9a09e7f1f30a008dbd65af8042d7b8d71fca5c454662e1becb941ef4c94b112112c0527fecebd47519
data/CHANGELOG CHANGED
@@ -1,4 +1,11 @@
1
- 0.4.5:
1
+ 0.7.0:
2
+ * Implement Authorization Header v4 (add config: `signature_version: 4`)
3
+ * Wrap message ID in `<>` correctly
4
+
5
+ 0.6.0:
6
+ * Various small bug fixes
7
+
8
+ 0.5.0:
2
9
  * Check for existence of html or text body when attachments are included
3
10
  * Message ids in mail objects should now reflect what is actually sent.
4
11
 
data/Gemfile CHANGED
@@ -6,13 +6,16 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
- gem "shoulda-context", ">= 0"
10
- gem "bundler"
11
- #gem "rcov", ">= 0"
9
+ gem "bundler", ">= 1.17"
12
10
  gem 'flexmock', '~> 0.8.11'
11
+ gem "jeweler"
12
+ gem "rake"
13
+ gem "shoulda-context", ">= 0"
14
+ gem 'test-unit'
15
+ gem 'timecop'
13
16
  end
14
17
 
15
- gem 'xml-simple'
16
18
  gem 'builder'
17
- gem 'mime-types'
18
19
  gem 'mail', '> 2.2.5'
20
+ gem 'mime-types'
21
+ gem 'xml-simple'
@@ -1,12 +1,63 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ addressable (2.4.0)
4
5
  builder (3.2.2)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ faraday (0.9.2)
9
+ multipart-post (>= 1.2, < 3)
5
10
  flexmock (0.8.11)
11
+ git (1.7.0)
12
+ rchardet (~> 1.8)
13
+ github_api (0.16.0)
14
+ addressable (~> 2.4.0)
15
+ descendants_tracker (~> 0.0.4)
16
+ faraday (~> 0.8, < 0.10)
17
+ hashie (>= 3.4)
18
+ mime-types (>= 1.16, < 3.0)
19
+ oauth2 (~> 1.0)
20
+ hashie (4.1.0)
21
+ highline (2.0.3)
22
+ jeweler (2.3.9)
23
+ builder
24
+ bundler
25
+ git (>= 1.2.5)
26
+ github_api (~> 0.16.0)
27
+ highline (>= 1.6.15)
28
+ nokogiri (>= 1.5.10)
29
+ psych
30
+ rake
31
+ rdoc
32
+ semver2
33
+ jwt (2.2.2)
6
34
  mail (2.6.1)
7
35
  mime-types (>= 1.16, < 3)
8
36
  mime-types (2.4.1)
37
+ mini_portile2 (2.4.0)
38
+ multi_json (1.15.0)
39
+ multi_xml (0.6.0)
40
+ multipart-post (2.1.1)
41
+ nokogiri (1.10.10)
42
+ mini_portile2 (~> 2.4.0)
43
+ oauth2 (1.4.4)
44
+ faraday (>= 0.8, < 2.0)
45
+ jwt (>= 1.0, < 3.0)
46
+ multi_json (~> 1.3)
47
+ multi_xml (~> 0.5)
48
+ rack (>= 1.2, < 3)
49
+ power_assert (1.2.0)
50
+ psych (3.1.0)
51
+ rack (2.2.3)
52
+ rake (13.0.1)
53
+ rchardet (1.8.0)
54
+ rdoc (6.2.0)
55
+ semver2 (3.4.2)
9
56
  shoulda-context (1.2.1)
57
+ test-unit (3.3.6)
58
+ power_assert
59
+ thread_safe (0.3.6)
60
+ timecop (0.9.1)
10
61
  xml-simple (1.1.4)
11
62
 
12
63
  PLATFORMS
@@ -14,9 +65,16 @@ PLATFORMS
14
65
 
15
66
  DEPENDENCIES
16
67
  builder
17
- bundler
68
+ bundler (>= 1.17)
18
69
  flexmock (~> 0.8.11)
70
+ jeweler
19
71
  mail (> 2.2.5)
20
72
  mime-types
73
+ rake
21
74
  shoulda-context
75
+ test-unit
76
+ timecop
22
77
  xml-simple
78
+
79
+ BUNDLED WITH
80
+ 1.17.3
data/README.erb CHANGED
@@ -16,11 +16,11 @@
16
16
 
17
17
  == Rails
18
18
 
19
- This gem is compatible with Rails >= 3.0.0 and Rails 2.3.x
19
+ This gem is compatible with Rails >= 3.0.0 and Ruby 2.3.x
20
20
 
21
21
  To use, first add the gem to your Gemfile:
22
22
 
23
- gem "aws-ses", "~> 0.6.0", :require => 'aws/ses'
23
+ gem "aws-ses", "~> 0.7.0", :require => 'aws/ses'
24
24
 
25
25
  == For Rails 3.x
26
26
 
@@ -28,7 +28,8 @@ Then, add your Amazon credentials and extend ActionMailer in `config/initializer
28
28
 
29
29
  ActionMailer::Base.add_delivery_method :ses, AWS::SES::Base,
30
30
  :access_key_id => 'abc',
31
- :secret_access_key => '123'
31
+ :secret_access_key => '123',
32
+ :signature_version => 4
32
33
 
33
34
  Then set the delivery method in `config/environments/*rb` as appropriate:
34
35
 
@@ -77,7 +78,7 @@ Available at: https://github.com/drewblas/aws-ses
77
78
 
78
79
  == Copyright
79
80
 
80
- Copyright (c) 2011 Drew Blas. See LICENSE for further details.
81
+ Copyright (c) 2020 Drew Blas. See LICENSE for further details.
81
82
 
82
83
  == Thanks
83
84
 
@@ -90,3 +91,5 @@ Special thanks to Marcel Molina Jr. for his creation of AWS::S3 which I used por
90
91
  * sshaw
91
92
  * teeparham (documentation)
92
93
  * pzb
94
+ * NicholasHely
95
+ * m33h
@@ -28,7 +28,8 @@ To connect to a different one, just pass it as a parameter to the AWS::SES::Base
28
28
  ses = AWS::SES::Base.new(
29
29
  :access_key_id => 'abc',
30
30
  :secret_access_key => '123',
31
- :server => 'email.eu-west-1.amazonaws.com'
31
+ :server => 'email.eu-west-1.amazonaws.com',
32
+ :message_id_domain => 'eu-west-1.amazonses.com'
32
33
  )
33
34
 
34
35
  == Send E-mail
@@ -134,14 +135,13 @@ will return:
134
135
  "Rejects"=>"0",
135
136
  "Complaints"=>"0"}]
136
137
 
137
-
138
138
  == Rails
139
139
 
140
- This gem is compatible with Rails >= 3.0.0 and Rails 2.3.x
140
+ This gem is compatible with Rails >= 3.0.0 and Ruby 2.3.x
141
141
 
142
142
  To use, first add the gem to your Gemfile:
143
143
 
144
- gem "aws-ses", "~> 0.6.0", :require => 'aws/ses'
144
+ gem "aws-ses", "~> 0.7.0", :require => 'aws/ses'
145
145
 
146
146
  == For Rails 3.x
147
147
 
@@ -149,9 +149,10 @@ Then, add your Amazon credentials and extend ActionMailer in `config/initializer
149
149
 
150
150
  ActionMailer::Base.add_delivery_method :ses, AWS::SES::Base,
151
151
  :access_key_id => 'abc',
152
- :secret_access_key => '123'
152
+ :secret_access_key => '123',
153
+ :signature_version => 4
153
154
 
154
- Then set the delivery method in `config/environments/*rb` as appropriate:
155
+ Then set the delivery method in `config/environments/*.rb` as appropriate:
155
156
 
156
157
  config.action_mailer.delivery_method = :ses
157
158
 
@@ -179,8 +180,12 @@ It means that you are not running with SSL enabled in ruby. Re-compile ruby wit
179
180
  If you are receiving this message and you HAVE verified the [source] please <b>check to be sure you are not in sandbox mode!</b>
180
181
  "Email address is not verified.MessageRejected (AWS::Error)"
181
182
  If you have not been granted production access, you will have to <b>verify all recipients</b> as well.
182
-
183
- http://docs.amazonwebservices.com/ses/2010-12-01/DeveloperGuide/index.html?InitialSetup.Customer.html
183
+
184
+ To verify email addresses and domains:
185
+ https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-addresses-and-domains.html
186
+
187
+ To request production access:
188
+ https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html
184
189
 
185
190
  == Source
186
191
 
data/Rakefile CHANGED
@@ -67,3 +67,17 @@ namespace :doc do
67
67
  end
68
68
  end
69
69
  end
70
+
71
+ require 'jeweler'
72
+ Jeweler::Tasks.new do |gem|
73
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
74
+ gem.name = "aws-ses"
75
+ gem.homepage = "http://github.com/drewblas/aws-ses"
76
+ gem.license = "MIT"
77
+ gem.summary = "Client library for Amazon's Simple Email Service's REST API"
78
+ gem.description = "Client library for Amazon's Simple Email Service's REST API"
79
+ gem.email = "drew.blas@gmail.com"
80
+ gem.authors = ["Drew Blas", "Marcel Molina Jr."]
81
+ # dependencies defined in Gemfile
82
+ end
83
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
@@ -2,17 +2,20 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: aws-ses 0.7.0 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
- s.name = "aws-ses"
8
- s.version = "0.6.0"
8
+ s.name = "aws-ses".freeze
9
+ s.version = "0.7.0"
9
10
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Drew Blas", "Marcel Molina Jr."]
12
- s.date = "2014-10-13"
13
- s.description = "Client library for Amazon's Simple Email Service's REST API"
14
- s.email = "drew.blas@gmail.com"
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Drew Blas".freeze, "Marcel Molina Jr.".freeze]
14
+ s.date = "2020-09-03"
15
+ s.description = "Client library for Amazon's Simple Email Service's REST API".freeze
16
+ s.email = "drew.blas@gmail.com".freeze
15
17
  s.extra_rdoc_files = [
18
+ "CHANGELOG",
16
19
  "LICENSE",
17
20
  "README.erb",
18
21
  "README.rdoc",
@@ -49,48 +52,51 @@ Gem::Specification.new do |s|
49
52
  "test/response_test.rb",
50
53
  "test/send_email_test.rb"
51
54
  ]
52
- s.homepage = "http://github.com/drewblas/aws-ses"
53
- s.licenses = ["MIT"]
54
- s.require_paths = ["lib"]
55
- s.rubygems_version = "1.8.19"
56
- s.summary = "Client library for Amazon's Simple Email Service's REST API"
57
- s.test_files = [
58
- "test/address_test.rb",
59
- "test/base_test.rb",
60
- "test/extensions_test.rb",
61
- "test/fixtures.rb",
62
- "test/helper.rb",
63
- "test/info_test.rb",
64
- "test/mocks/fake_response.rb",
65
- "test/response_test.rb",
66
- "test/send_email_test.rb"
67
- ]
55
+ s.homepage = "http://github.com/drewblas/aws-ses".freeze
56
+ s.licenses = ["MIT".freeze]
57
+ s.rubygems_version = "2.5.2.3".freeze
58
+ s.summary = "Client library for Amazon's Simple Email Service's REST API".freeze
68
59
 
69
60
  if s.respond_to? :specification_version then
70
- s.specification_version = 3
61
+ s.specification_version = 4
71
62
 
72
63
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
73
- s.add_runtime_dependency(%q<xml-simple>, [">= 0"])
74
- s.add_runtime_dependency(%q<builder>, [">= 0"])
75
- s.add_runtime_dependency(%q<mime-types>, [">= 0"])
76
- s.add_runtime_dependency(%q<mail>, ["> 2.2.5"])
77
- s.add_development_dependency(%q<shoulda-context>, [">= 0"])
78
- s.add_development_dependency(%q<flexmock>, ["~> 0.8.11"])
64
+ s.add_runtime_dependency(%q<builder>.freeze, [">= 0"])
65
+ s.add_runtime_dependency(%q<mail>.freeze, ["> 2.2.5"])
66
+ s.add_runtime_dependency(%q<mime-types>.freeze, [">= 0"])
67
+ s.add_runtime_dependency(%q<xml-simple>.freeze, [">= 0"])
68
+ s.add_development_dependency(%q<bundler>.freeze, [">= 1.17"])
69
+ s.add_development_dependency(%q<flexmock>.freeze, ["~> 0.8.11"])
70
+ s.add_development_dependency(%q<jeweler>.freeze, [">= 0"])
71
+ s.add_development_dependency(%q<rake>.freeze, [">= 0"])
72
+ s.add_development_dependency(%q<shoulda-context>.freeze, [">= 0"])
73
+ s.add_development_dependency(%q<test-unit>.freeze, [">= 0"])
74
+ s.add_development_dependency(%q<timecop>.freeze, [">= 0"])
79
75
  else
80
- s.add_dependency(%q<xml-simple>, [">= 0"])
81
- s.add_dependency(%q<builder>, [">= 0"])
82
- s.add_dependency(%q<mime-types>, [">= 0"])
83
- s.add_dependency(%q<mail>, ["> 2.2.5"])
84
- s.add_dependency(%q<shoulda-context>, [">= 0"])
85
- s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
76
+ s.add_dependency(%q<builder>.freeze, [">= 0"])
77
+ s.add_dependency(%q<mail>.freeze, ["> 2.2.5"])
78
+ s.add_dependency(%q<mime-types>.freeze, [">= 0"])
79
+ s.add_dependency(%q<xml-simple>.freeze, [">= 0"])
80
+ s.add_dependency(%q<bundler>.freeze, [">= 1.17"])
81
+ s.add_dependency(%q<flexmock>.freeze, ["~> 0.8.11"])
82
+ s.add_dependency(%q<jeweler>.freeze, [">= 0"])
83
+ s.add_dependency(%q<rake>.freeze, [">= 0"])
84
+ s.add_dependency(%q<shoulda-context>.freeze, [">= 0"])
85
+ s.add_dependency(%q<test-unit>.freeze, [">= 0"])
86
+ s.add_dependency(%q<timecop>.freeze, [">= 0"])
86
87
  end
87
88
  else
88
- s.add_dependency(%q<xml-simple>, [">= 0"])
89
- s.add_dependency(%q<builder>, [">= 0"])
90
- s.add_dependency(%q<mime-types>, [">= 0"])
91
- s.add_dependency(%q<mail>, ["> 2.2.5"])
92
- s.add_dependency(%q<shoulda-context>, [">= 0"])
93
- s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
89
+ s.add_dependency(%q<builder>.freeze, [">= 0"])
90
+ s.add_dependency(%q<mail>.freeze, ["> 2.2.5"])
91
+ s.add_dependency(%q<mime-types>.freeze, [">= 0"])
92
+ s.add_dependency(%q<xml-simple>.freeze, [">= 0"])
93
+ s.add_dependency(%q<bundler>.freeze, [">= 1.17"])
94
+ s.add_dependency(%q<flexmock>.freeze, ["~> 0.8.11"])
95
+ s.add_dependency(%q<jeweler>.freeze, [">= 0"])
96
+ s.add_dependency(%q<rake>.freeze, [">= 0"])
97
+ s.add_dependency(%q<shoulda-context>.freeze, [">= 0"])
98
+ s.add_dependency(%q<test-unit>.freeze, [">= 0"])
99
+ s.add_dependency(%q<timecop>.freeze, [">= 0"])
94
100
  end
95
101
  end
96
102
 
@@ -26,14 +26,22 @@ module AWS #:nodoc:
26
26
  # ses = AWS::SES::Base.new(
27
27
  # :access_key_id => 'abc',
28
28
  # :secret_access_key => '123',
29
- # :server => 'email.eu-west-1.amazonaws.com'
29
+ # :server => 'email.eu-west-1.amazonaws.com',
30
+ # :message_id_domain => 'eu-west-1.amazonses.com'
30
31
  # )
32
+ #
31
33
 
32
34
  module SES
33
35
 
34
36
  API_VERSION = '2010-12-01'
35
-
37
+
38
+ DEFAULT_REGION = 'us-east-1'
39
+
40
+ SERVICE = 'ec2'
41
+
36
42
  DEFAULT_HOST = 'email.us-east-1.amazonaws.com'
43
+
44
+ DEFAULT_MESSAGE_ID_DOMAIN = 'email.amazonses.com'
37
45
 
38
46
  USER_AGENT = 'github-aws-ses-ruby-gem'
39
47
 
@@ -67,13 +75,17 @@ module AWS #:nodoc:
67
75
  def SES.authorization_header(key, alg, sig)
68
76
  "AWS3-HTTPS AWSAccessKeyId=#{key}, Algorithm=#{alg}, Signature=#{sig}"
69
77
  end
70
-
78
+
79
+ def SES.authorization_header_v4(credential, signed_headers, signature)
80
+ "AWS4-HMAC-SHA256 Credential=#{credential}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
81
+ end
82
+
71
83
  # AWS::SES::Base is the abstract super class of all classes who make requests against SES
72
84
  class Base
73
85
  include SendEmail
74
86
  include Info
75
87
 
76
- attr_reader :use_ssl, :server, :proxy_server, :port
88
+ attr_reader :use_ssl, :server, :proxy_server, :port, :message_id_domain, :signature_version, :region
77
89
  attr_accessor :settings
78
90
 
79
91
  # @option options [String] :access_key_id ("") The user's AWS Access Key ID
@@ -82,6 +94,8 @@ module AWS #:nodoc:
82
94
  # @option options [String] :server ("email.us-east-1.amazonaws.com") The server API endpoint host
83
95
  # @option options [String] :proxy_server (nil) An HTTP proxy server FQDN
84
96
  # @option options [String] :user_agent ("github-aws-ses-ruby-gem") The HTTP User-Agent header value
97
+ # @option options [String] :region ("us-east-1") The server API endpoint host
98
+ # @option options [String] :message_id_domain ("us-east-1.amazonses.com") Domain used to build message_id header
85
99
  # @return [Object] the object.
86
100
  def initialize( options = {} )
87
101
 
@@ -89,16 +103,21 @@ module AWS #:nodoc:
89
103
  :secret_access_key => "",
90
104
  :use_ssl => true,
91
105
  :server => DEFAULT_HOST,
106
+ :message_id_domain => DEFAULT_MESSAGE_ID_DOMAIN,
92
107
  :path => "/",
93
108
  :user_agent => USER_AGENT,
94
- :proxy_server => nil
109
+ :proxy_server => nil,
110
+ :region => DEFAULT_REGION
95
111
  }.merge(options)
96
112
 
113
+ @signature_version = options[:signature_version] || 2
97
114
  @server = options[:server]
115
+ @message_id_domain = options[:message_id_domain]
98
116
  @proxy_server = options[:proxy_server]
99
117
  @use_ssl = options[:use_ssl]
100
118
  @path = options[:path]
101
119
  @user_agent = options[:user_agent]
120
+ @region = options[:region]
102
121
  @settings = {}
103
122
 
104
123
  raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
@@ -130,10 +149,6 @@ module AWS #:nodoc:
130
149
  proxy.password).new(options[:server], @port)
131
150
 
132
151
  @http.use_ssl = @use_ssl
133
-
134
- # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
135
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
136
-
137
152
  end
138
153
 
139
154
  def connection
@@ -150,7 +165,7 @@ module AWS #:nodoc:
150
165
  timestamp = Time.now.getutc
151
166
 
152
167
  params.merge!( {"Action" => action,
153
- "SignatureVersion" => "2",
168
+ "SignatureVersion" => signature_version.to_s,
154
169
  "SignatureMethod" => 'HmacSHA256',
155
170
  "AWSAccessKeyId" => @access_key_id,
156
171
  "Version" => API_VERSION,
@@ -162,9 +177,9 @@ module AWS #:nodoc:
162
177
 
163
178
  req = {}
164
179
 
165
- req['X-Amzn-Authorization'] = get_aws_auth_param(timestamp.httpdate, @secret_access_key)
180
+ req['X-Amzn-Authorization'] = get_aws_auth_param(timestamp.httpdate, @secret_access_key, action, signature_version.to_s)
166
181
  req['Date'] = timestamp.httpdate
167
- req['User-Agent'] = @user_agent
182
+ req['User-Agent'] = @user_agent
168
183
 
169
184
  response = connection.post(@path, query, req)
170
185
 
@@ -179,9 +194,73 @@ module AWS #:nodoc:
179
194
  end
180
195
 
181
196
  # Set the Authorization header using AWS signed header authentication
182
- def get_aws_auth_param(timestamp, secret_access_key)
197
+ def get_aws_auth_param(timestamp, secret_access_key, action = '', signature_version = 2)
183
198
  encoded_canonical = SES.encode(secret_access_key, timestamp, false)
184
- SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical)
199
+ return SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical) unless signature_version == 4
200
+
201
+ SES.authorization_header_v4(sig_v4_auth_credential, sig_v4_auth_signed_headers, sig_v4_auth_signature(action))
202
+ end
203
+
204
+ private
205
+
206
+ def sig_v4_auth_credential
207
+ @access_key_id + '/' + credential_scope
208
+ end
209
+
210
+ def sig_v4_auth_signed_headers
211
+ 'host;x-amz-date'
212
+ end
213
+
214
+ def credential_scope
215
+ datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request'
216
+ end
217
+
218
+ def string_to_sign(for_action)
219
+ "AWS4-HMAC-SHA256\n" + amzdate + "\n" + credential_scope + "\n" + Digest::SHA256.hexdigest(canonical_request(for_action).encode('utf-8').b)
220
+ end
221
+
222
+
223
+ def amzdate
224
+ Time.now.utc.strftime('%Y%m%dT%H%M%SZ')
225
+ end
226
+
227
+ def datestamp
228
+ Time.now.utc.strftime('%Y%m%d')
229
+ end
230
+
231
+ def canonical_request(for_action)
232
+ "GET" + "\n" + "/" + "\n" + canonical_querystring(for_action) + "\n" + canonical_headers + "\n" + sig_v4_auth_signed_headers + "\n" + payload_hash
233
+ end
234
+
235
+ def canonical_querystring(action)
236
+ "Action=#{action}&Version=2013-10-15"
237
+ end
238
+
239
+ def canonical_headers
240
+ 'host:' + server + "\n" + 'x-amz-date:' + amzdate + "\n"
241
+ end
242
+
243
+ def payload_hash
244
+ Digest::SHA256.hexdigest(''.encode('utf-8'))
245
+ end
246
+
247
+ def sig_v4_auth_signature(for_action)
248
+ signing_key = getSignatureKey(@secret_access_key, datestamp, region, SERVICE)
249
+
250
+ OpenSSL::HMAC.hexdigest("SHA256", signing_key, string_to_sign(for_action).encode('utf-8'))
251
+ end
252
+
253
+ def getSignatureKey(key, dateStamp, regionName, serviceName)
254
+ kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
255
+ kRegion = sign(kDate, regionName)
256
+ kService = sign(kRegion, serviceName)
257
+ kSigning = sign(kService, 'aws4_request')
258
+
259
+ kSigning
260
+ end
261
+
262
+ def sign(key, msg)
263
+ OpenSSL::HMAC.digest("SHA256", key, msg.encode('utf-8'))
185
264
  end
186
265
  end # class Base
187
266
  end # Module SES
@@ -101,7 +101,7 @@ module AWS
101
101
 
102
102
  raw_email = build_raw_email(message, args)
103
103
  result = request('SendRawEmail', raw_email)
104
- message.message_id = "#{result.parsed['SendRawEmailResult']['MessageId']}@email.amazonses.com"
104
+ message.message_id = "<#{result.parsed['SendRawEmailResult']['MessageId']}@#{message_id_domain}>"
105
105
  result
106
106
  end
107
107
 
@@ -37,4 +37,69 @@ class BaseTest < Test::Unit::TestCase
37
37
  # assert result.error.error?
38
38
  # assert_equal 'ValidationError', result.error.code
39
39
  end
40
+
41
+ def test_ses_authorization_header_v2
42
+ aws_access_key_id = 'fake_aws_key_id'
43
+ aws_secret_access_key = 'fake_aws_access_key'
44
+ timestamp = Time.new(2020, 7, 2, 7, 17, 58, '+00:00')
45
+
46
+ base = ::AWS::SES::Base.new(
47
+ access_key_id: aws_access_key_id,
48
+ secret_access_key: aws_secret_access_key
49
+ )
50
+
51
+ assert_equal 'AWS3-HTTPS AWSAccessKeyId=fake_aws_key_id, Algorithm=HmacSHA256, Signature=eHh/cPIJJUc1+RMCueAi50EPlYxkZNXMrxtGxjkBD1w=', base.get_aws_auth_param(timestamp.httpdate, aws_secret_access_key)
52
+ end
53
+
54
+ def test_ses_authorization_header_v4
55
+ aws_access_key_id = 'fake_aws_key_id'
56
+ aws_secret_access_key = 'fake_aws_access_key'
57
+ time = Time.new(2020, 7, 2, 7, 17, 58, '+00:00')
58
+ ::Timecop.freeze(time)
59
+
60
+ base = ::AWS::SES::Base.new(
61
+ server: 'ec2.amazonaws.com',
62
+ signature_version: 4,
63
+ access_key_id: aws_access_key_id,
64
+ secret_access_key: aws_secret_access_key
65
+ )
66
+
67
+ assert_equal 'AWS4-HMAC-SHA256 Credential=fake_aws_key_id/20200702/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=c0465b36efd110b14a1c6dcca3e105085ed2bfb2a3fd3b3586cc459326ab43aa', base.get_aws_auth_param(time.httpdate, aws_secret_access_key, 'DescribeRegions', 4)
68
+ Timecop.return
69
+ end
70
+
71
+ def test_ses_authorization_header_v4_changed_host
72
+ aws_access_key_id = 'fake_aws_key_id'
73
+ aws_secret_access_key = 'fake_aws_access_key'
74
+ time = Time.new(2020, 7, 2, 7, 17, 58, '+00:00')
75
+ ::Timecop.freeze(time)
76
+
77
+ base = ::AWS::SES::Base.new(
78
+ server: 'email.us-east-1.amazonaws.com',
79
+ signature_version: 4,
80
+ access_key_id: aws_access_key_id,
81
+ secret_access_key: aws_secret_access_key
82
+ )
83
+
84
+ assert_equal 'AWS4-HMAC-SHA256 Credential=fake_aws_key_id/20200702/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=b872601457070ab98e7038bdcd4dc1f5eab586ececf9908525474408b0740515', base.get_aws_auth_param(time.httpdate, aws_secret_access_key, 'DescribeRegions', 4)
85
+ Timecop.return
86
+ end
87
+
88
+ def test_ses_authorization_header_v4_changed_region
89
+ aws_access_key_id = 'fake_aws_key_id'
90
+ aws_secret_access_key = 'fake_aws_access_key'
91
+ time = Time.new(2020, 7, 2, 7, 17, 58, '+00:00')
92
+ ::Timecop.freeze(time)
93
+
94
+ base = ::AWS::SES::Base.new(
95
+ server: 'email.us-east-1.amazonaws.com',
96
+ signature_version: 4,
97
+ access_key_id: aws_access_key_id,
98
+ secret_access_key: aws_secret_access_key,
99
+ region: 'eu-west-1'
100
+ )
101
+
102
+ assert_not_equal 'AWS4-HMAC-SHA256 Credential=fake_aws_key_id/20200702/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=b872601457070ab98e7038bdcd4dc1f5eab586ececf9908525474408b0740515', base.get_aws_auth_param(time.httpdate, aws_secret_access_key, 'DescribeRegions', 4)
103
+ Timecop.return
104
+ end
40
105
  end
@@ -24,6 +24,7 @@ require File.dirname(__FILE__) + '/fixtures'
24
24
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
25
25
  $LOAD_PATH.unshift(File.dirname(__FILE__))
26
26
  require 'aws/ses'
27
+ require 'timecop'
27
28
 
28
29
  class Test::Unit::TestCase
29
30
  require 'net/http'
@@ -82,6 +82,27 @@ class SendEmailTest < Test::Unit::TestCase
82
82
  assert_equal 'xyz-123', result.request_id
83
83
  end
84
84
 
85
+ should 'send a raw e-mail with a mail object' do
86
+ mock_connection(@base, :body => %{
87
+ <SendRawEmailResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
88
+ <SendRawEmailResult>
89
+ <MessageId>abc-123</MessageId>
90
+ </SendRawEmailResult>
91
+ <ResponseMetadata>
92
+ <RequestId>xyz-123</RequestId>
93
+ </ResponseMetadata>
94
+ </SendRawEmailResponse>
95
+ })
96
+
97
+ message = Mail.new(@basic_email)
98
+ result = @base.send_raw_email(message)
99
+ assert result.success?
100
+ assert_equal 'abc-123', result.message_id
101
+ assert_equal 'xyz-123', result.request_id
102
+ assert message.errors.empty?
103
+ assert_equal 'abc-123@email.amazonses.com', message.message_id
104
+ end
105
+
85
106
  context "with a standard email" do
86
107
  setup do
87
108
  @message = Mail.new({:from => 'jon@example.com', :to => 'dave@example.com', :cc => 'sally@example.com', :subject => 'Subject1', :body => "test body"})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-ses
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drew Blas
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-13 00:00:00.000000000 Z
12
+ date: 2020-09-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: xml-simple
15
+ name: builder
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
@@ -26,19 +26,19 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
- name: builder
29
+ name: mail
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - ">"
33
33
  - !ruby/object:Gem::Version
34
- version: '0'
34
+ version: 2.2.5
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - ">"
40
40
  - !ruby/object:Gem::Version
41
- version: '0'
41
+ version: 2.2.5
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: mime-types
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -54,33 +54,33 @@ dependencies:
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: mail
57
+ name: xml-simple
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">"
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: 2.2.5
62
+ version: '0'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">"
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: 2.2.5
69
+ version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
- name: shoulda-context
71
+ name: bundler
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: '0'
76
+ version: '1.17'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
- version: '0'
83
+ version: '1.17'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: flexmock
86
86
  requirement: !ruby/object:Gem::Requirement
@@ -95,11 +95,82 @@ dependencies:
95
95
  - - "~>"
96
96
  - !ruby/object:Gem::Version
97
97
  version: 0.8.11
98
+ - !ruby/object:Gem::Dependency
99
+ name: jeweler
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rake
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: shoulda-context
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: test-unit
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: timecop
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
98
168
  description: Client library for Amazon's Simple Email Service's REST API
99
169
  email: drew.blas@gmail.com
100
170
  executables: []
101
171
  extensions: []
102
172
  extra_rdoc_files:
173
+ - CHANGELOG
103
174
  - LICENSE
104
175
  - README.erb
105
176
  - README.rdoc
@@ -154,17 +225,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
225
  version: '0'
155
226
  requirements: []
156
227
  rubyforge_project:
157
- rubygems_version: 2.2.2
228
+ rubygems_version: 2.5.2.3
158
229
  signing_key:
159
- specification_version: 3
230
+ specification_version: 4
160
231
  summary: Client library for Amazon's Simple Email Service's REST API
161
- test_files:
162
- - test/address_test.rb
163
- - test/base_test.rb
164
- - test/extensions_test.rb
165
- - test/fixtures.rb
166
- - test/helper.rb
167
- - test/info_test.rb
168
- - test/mocks/fake_response.rb
169
- - test/response_test.rb
170
- - test/send_email_test.rb
232
+ test_files: []