aws-ses-v4 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'erb'
13
+
14
+ require 'rake/testtask'
15
+ Rake::TestTask.new(:test) do |test|
16
+ test.libs << 'lib' << 'test'
17
+ test.pattern = 'test/**/*_test.rb'
18
+ test.verbose = true
19
+ end
20
+
21
+ # require 'rcov/rcovtask'
22
+ # Rcov::RcovTask.new do |test|
23
+ # test.libs << 'test'
24
+ # test.pattern = 'test/**/*_test.rb'
25
+ # test.verbose = true
26
+ # end
27
+
28
+ task :default => :test
29
+
30
+ require 'rdoc/task'
31
+ require File.dirname(__FILE__) + '/lib/aws/ses'
32
+
33
+ namespace :doc do
34
+ Rake::RDocTask.new do |rdoc|
35
+ rdoc.rdoc_dir = 'doc'
36
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
37
+ rdoc.title = "AWS::SES -- Support for Amazon SES's REST api #{version}"
38
+ rdoc.options << '--line-numbers' << '--inline-source'
39
+ rdoc.rdoc_files.include('README.rdoc')
40
+ rdoc.rdoc_files.include('LICENSE')
41
+ rdoc.rdoc_files.include('CHANGELOG')
42
+ rdoc.rdoc_files.include('TODO')
43
+ rdoc.rdoc_files.include('VERSION')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
46
+
47
+ task :rdoc => 'doc:readme'
48
+
49
+ task :refresh => :rerdoc do
50
+ system 'open doc/index.html'
51
+ end
52
+
53
+ desc "Generate readme.rdoc from readme.erb"
54
+ task :readme do
55
+ require 'support/rdoc/code_info'
56
+ RDoc::CodeInfo.parse('lib/**/*.rb')
57
+
58
+ strip_comments = lambda {|comment| comment.gsub(/^# ?/, '')}
59
+ docs_for = lambda do |location|
60
+ info = RDoc::CodeInfo.for(location)
61
+ raise RuntimeError, "Couldn't find documentation for `#{location}'" unless info
62
+ strip_comments[info.comment]
63
+ end
64
+
65
+ open('README.rdoc', 'w') do |file|
66
+ file.write ERB.new(IO.read('README.erb')).result(binding)
67
+ end
68
+ end
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/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Use a better XML parser (and be consistent)
2
+ * Rename Base to something else (probably Mailer): Nothing else actually inherits from Base, so that is a very poor naming convention. I intend to change it in the future
3
+ * Integer responses should probably be cast to ints instead of left as strings
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.1
data/aws-ses.gemspec ADDED
@@ -0,0 +1,102 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: aws-ses 0.7.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "aws-ses-v4".freeze
9
+ s.version = "0.8.1"
10
+
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 = "2021-04-10"
15
+ s.description = "Client library for Amazon's Simple Email Service's REST API".freeze
16
+ s.email = "drew.blas@gmail.com".freeze
17
+ s.extra_rdoc_files = [
18
+ "CHANGELOG",
19
+ "LICENSE",
20
+ "README.erb",
21
+ "README.rdoc",
22
+ "TODO"
23
+ ]
24
+ s.files = [
25
+ ".document",
26
+ "CHANGELOG",
27
+ "Gemfile",
28
+ "Gemfile.lock",
29
+ "LICENSE",
30
+ "README.erb",
31
+ "README.rdoc",
32
+ "Rakefile",
33
+ "TODO",
34
+ "VERSION",
35
+ "aws-ses.gemspec",
36
+ "lib/aws/actionmailer/ses_extension.rb",
37
+ "lib/aws/ses.rb",
38
+ "lib/aws/ses/addresses.rb",
39
+ "lib/aws/ses/base.rb",
40
+ "lib/aws/ses/extensions.rb",
41
+ "lib/aws/ses/info.rb",
42
+ "lib/aws/ses/response.rb",
43
+ "lib/aws/ses/send_email.rb",
44
+ "lib/aws/ses/version.rb",
45
+ "test/address_test.rb",
46
+ "test/base_test.rb",
47
+ "test/extensions_test.rb",
48
+ "test/fixtures.rb",
49
+ "test/helper.rb",
50
+ "test/info_test.rb",
51
+ "test/mocks/fake_response.rb",
52
+ "test/response_test.rb",
53
+ "test/send_email_test.rb"
54
+ ]
55
+ s.homepage = "http://github.com/sertangulveren/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
59
+
60
+ if s.respond_to? :specification_version then
61
+ s.specification_version = 4
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
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"])
75
+ else
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"])
87
+ end
88
+ else
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"])
100
+ end
101
+ end
102
+
@@ -0,0 +1,19 @@
1
+ # A quick little extension to use this lib with with rails 2.3.X
2
+ # To use it, in your environment.rb or some_environment.rb you simply set
3
+ #
4
+ # config.after_initialize do
5
+ # ActionMailer::Base.delivery_method = :amazon_ses
6
+ # ActionMailer::Base.custom_amazon_ses_mailer = AWS::SES::Base.new(:secret_access_key => S3_CONFIG[:secret_access_key], :access_key_id => S3_CONFIG[:access_key_id])
7
+ # end
8
+
9
+ module ActionMailer
10
+ class Base
11
+ cattr_accessor :custom_amazon_ses_mailer
12
+
13
+ def perform_delivery_amazon_ses(mail)
14
+ raise 'AWS::SES::Base has not been intitialized.' unless @@custom_amazon_ses_mailer
15
+ @@custom_amazon_ses_mailer.deliver!(mail)
16
+ end
17
+
18
+ end
19
+ end
data/lib/aws/ses.rb ADDED
@@ -0,0 +1,28 @@
1
+ %w[ base64 cgi openssl digest/sha1 net/https net/http rexml/document time ostruct mail].each { |f| require f }
2
+
3
+ begin
4
+ require 'URI' unless defined? URI
5
+ rescue Exception => e
6
+ # nothing
7
+ end
8
+
9
+ begin
10
+ require 'xmlsimple' unless defined? XmlSimple
11
+ rescue Exception => e
12
+ require 'xml-simple' unless defined? XmlSimple
13
+ end
14
+
15
+ $:.unshift(File.dirname(__FILE__))
16
+ require 'ses/extensions'
17
+
18
+ require 'ses/response'
19
+ require 'ses/send_email'
20
+ require 'ses/info'
21
+ require 'ses/base'
22
+ require 'ses/version'
23
+ require 'ses/addresses'
24
+
25
+ if defined?(Rails)
26
+ major, minor = Rails.version.to_s.split('.')
27
+ require 'actionmailer/ses_extension' if major == '2' && minor == '3'
28
+ end
@@ -0,0 +1,75 @@
1
+ module AWS
2
+ module SES
3
+ # AWS::SES::Addresses provides for:
4
+ # * Listing verified e-mail addresses
5
+ # * Adding new e-mail addresses to verify
6
+ # * Deleting verified e-mail addresses
7
+ #
8
+ # You can access these methods as follows:
9
+ #
10
+ # ses = AWS::SES::Base.new( ... connection info ... )
11
+ #
12
+ # # Get a list of verified addresses
13
+ # ses.addresses.list.result
14
+ #
15
+ # # Add a new e-mail address to verify
16
+ # ses.addresses.verify('jon@example.com')
17
+ #
18
+ # # Delete an e-mail address
19
+ # ses.addresses.delete('jon@example.com')
20
+ class Addresses < Base
21
+ def initialize(ses)
22
+ @ses = ses
23
+ end
24
+
25
+ # List all verified e-mail addresses
26
+ #
27
+ # Usage:
28
+ # ses.addresses.list.result
29
+ # =>
30
+ # ['email1@example.com', email2@example.com']
31
+ def list
32
+ @ses.request('ListVerifiedEmailAddresses')
33
+ end
34
+
35
+ def verify(email)
36
+ @ses.request('VerifyEmailAddress',
37
+ 'EmailAddress' => email
38
+ )
39
+ end
40
+
41
+ def delete(email)
42
+ @ses.request('DeleteVerifiedEmailAddress',
43
+ 'EmailAddress' => email
44
+ )
45
+ end
46
+ end
47
+
48
+ class ListVerifiedEmailAddressesResponse < AWS::SES::Response
49
+ def result
50
+ if members = parsed['ListVerifiedEmailAddressesResult']['VerifiedEmailAddresses']
51
+ [members['member']].flatten
52
+ else
53
+ []
54
+ end
55
+ end
56
+ memoized :result
57
+ end
58
+
59
+ class VerifyEmailAddressResponse < AWS::SES::Response
60
+ end
61
+
62
+ class DeleteVerifiedEmailAddressResponse < AWS::SES::Response
63
+ def result
64
+ success?
65
+ end
66
+ end
67
+
68
+ class Base
69
+ def addresses
70
+ @addresses ||= Addresses.new(self)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,286 @@
1
+ module AWS #:nodoc:
2
+ # AWS::SES is a Ruby library for Amazon's Simple Email Service's REST API (http://aws.amazon.com/ses).
3
+ #
4
+ # == Getting started
5
+ #
6
+ # To get started you need to require 'aws/ses':
7
+ #
8
+ # % irb -rubygems
9
+ # irb(main):001:0> require 'aws/ses'
10
+ # # => true
11
+ #
12
+ # Before you can do anything, you must establish a connection using Base.new. A basic connection would look something like this:
13
+ #
14
+ # ses = AWS::SES::Base.new(
15
+ # :access_key_id => 'abc',
16
+ # :secret_access_key => '123'
17
+ # )
18
+ #
19
+ # The minimum connection options that you must specify are your access key id and your secret access key.
20
+ #
21
+ # === Connecting to a server from another region
22
+ #
23
+ # The default server API endpoint is "email.us-east-1.amazonaws.com", corresponding to the US East 1 region.
24
+ # To connect to a different one, just pass it as a parameter to the AWS::SES::Base initializer:
25
+ #
26
+ # ses = AWS::SES::Base.new(
27
+ # :access_key_id => 'abc',
28
+ # :secret_access_key => '123',
29
+ # :server => 'email.eu-west-1.amazonaws.com',
30
+ # :message_id_domain => 'eu-west-1.amazonses.com'
31
+ # )
32
+ #
33
+
34
+ module SES
35
+
36
+ API_VERSION = '2010-12-01'
37
+
38
+ DEFAULT_REGION = 'us-east-1'
39
+
40
+ SERVICE = 'ses'
41
+
42
+ DEFAULT_HOST = 'email.us-east-1.amazonaws.com'
43
+
44
+ DEFAULT_MESSAGE_ID_DOMAIN = 'email.amazonses.com'
45
+
46
+ USER_AGENT = 'github-aws-ses-ruby-gem'
47
+
48
+ DEFAULT_SIGNATURE_VERSION = 4
49
+
50
+ # Encodes the given string with the secret_access_key by taking the
51
+ # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
52
+ # url encode the result of that to protect the string if it's going to
53
+ # be used as a query string parameter.
54
+ #
55
+ # @param [String] secret_access_key the user's secret access key for signing.
56
+ # @param [String] str the string to be hashed and encoded.
57
+ # @param [Boolean] urlencode whether or not to url encode the result., true or false
58
+ # @return [String] the signed and encoded string.
59
+ def SES.encode(secret_access_key, str, urlencode=true)
60
+ digest = OpenSSL::Digest.new('sha256')
61
+ b64_hmac =
62
+ Base64.encode64(
63
+ OpenSSL::HMAC.digest(digest, secret_access_key, str)).gsub("\n","")
64
+
65
+ if urlencode
66
+ return CGI::escape(b64_hmac)
67
+ else
68
+ return b64_hmac
69
+ end
70
+ end
71
+
72
+ # Generates the HTTP Header String that Amazon looks for
73
+ #
74
+ # @param [String] key the AWS Access Key ID
75
+ # @param [String] alg the algorithm used for the signature
76
+ # @param [String] sig the signature itself
77
+ def SES.authorization_header(key, alg, sig)
78
+ "AWS3-HTTPS AWSAccessKeyId=#{key}, Algorithm=#{alg}, Signature=#{sig}"
79
+ end
80
+
81
+ def SES.authorization_header_v4(credential, signed_headers, signature)
82
+ "AWS4-HMAC-SHA256 Credential=#{credential}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
83
+ end
84
+
85
+ # AWS::SES::Base is the abstract super class of all classes who make requests against SES
86
+ class Base
87
+ include SendEmail
88
+ include Info
89
+
90
+ attr_reader :use_ssl, :server, :proxy_server, :port, :message_id_domain, :signature_version, :region,
91
+ :action, :action_time, :query
92
+ attr_accessor :settings
93
+
94
+ # @option options [String] :access_key_id ("") The user's AWS Access Key ID
95
+ # @option options [String] :secret_access_key ("") The user's AWS Secret Access Key
96
+ # @option options [Boolean] :use_ssl (true) Connect using SSL?
97
+ # @option options [String] :server ("email.us-east-1.amazonaws.com") The server API endpoint host
98
+ # @option options [String] :proxy_server (nil) An HTTP proxy server FQDN
99
+ # @option options [String] :user_agent ("github-aws-ses-ruby-gem") The HTTP User-Agent header value
100
+ # @option options [String] :region ("us-east-1") The server API endpoint host
101
+ # @option options [String] :message_id_domain ("us-east-1.amazonses.com") Domain used to build message_id header
102
+ # @return [Object] the object.
103
+ def initialize( options = {} )
104
+
105
+ options = { :access_key_id => "",
106
+ :secret_access_key => "",
107
+ :use_ssl => true,
108
+ :server => DEFAULT_HOST,
109
+ :message_id_domain => DEFAULT_MESSAGE_ID_DOMAIN,
110
+ :path => "/",
111
+ :user_agent => USER_AGENT,
112
+ :proxy_server => nil,
113
+ :region => DEFAULT_REGION,
114
+ :signature_version => DEFAULT_SIGNATURE_VERSION
115
+ }.merge(options)
116
+
117
+ @signature_version = options[:signature_version]
118
+ @server = options[:server]
119
+ @message_id_domain = options[:message_id_domain]
120
+ @proxy_server = options[:proxy_server]
121
+ @use_ssl = options[:use_ssl]
122
+ @path = options[:path]
123
+ @user_agent = options[:user_agent]
124
+ @region = options[:region]
125
+ @settings = {}
126
+
127
+ raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
128
+ raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
129
+ raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
130
+ raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
131
+ raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
132
+ raise ArgumentError, ":signature_version must be 2 or 4" unless [2, 4].include?(options[:signature_version])
133
+
134
+ if options[:port]
135
+ # user-specified port
136
+ @port = options[:port]
137
+ elsif @use_ssl
138
+ # https
139
+ @port = 443
140
+ else
141
+ # http
142
+ @port = 80
143
+ end
144
+
145
+ @access_key_id = options[:access_key_id]
146
+ @secret_access_key = options[:secret_access_key]
147
+
148
+ # Use proxy server if defined
149
+ # Based on patch by Mathias Dalheimer. 20070217
150
+ proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
151
+ @http = Net::HTTP::Proxy( proxy.host,
152
+ proxy.port,
153
+ proxy.user,
154
+ proxy.password).new(options[:server], @port)
155
+
156
+ @http.use_ssl = @use_ssl
157
+ end
158
+
159
+ def connection
160
+ @http
161
+ end
162
+
163
+ # Make the connection to AWS passing in our request.
164
+ # allow us to have a one line call in each method which will do all of the work
165
+ # in making the actual request to AWS.
166
+ def request(action, params = {})
167
+ @action = action
168
+ # Use a copy so that we don't modify the caller's Hash, remove any keys that have nil or empty values
169
+ params = params.reject { |_, value| value.nil? or value.empty?}
170
+
171
+ @action_time = Time.now.getutc
172
+
173
+ params.merge!( {"Action" => action,
174
+ "SignatureVersion" => signature_version.to_s,
175
+ "SignatureMethod" => 'HmacSHA256',
176
+ "AWSAccessKeyId" => @access_key_id,
177
+ "Version" => API_VERSION,
178
+ "Timestamp" => action_time.iso8601 } )
179
+
180
+ @query = params.sort.collect do |param|
181
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
182
+ end.join("&")
183
+ response = connection.post(@path, query, get_req_headers)
184
+
185
+ response_class = AWS::SES.const_get( "#{action}Response" )
186
+ result = response_class.new(action, response)
187
+
188
+ if result.error?
189
+ raise ResponseError.new(result)
190
+ end
191
+
192
+ result
193
+ end
194
+
195
+ def get_req_headers
196
+ headers = {}
197
+ if signature_version == 4
198
+ headers['host'] = @server
199
+ headers['authorization'] = get_aws_auth_header_v4
200
+ headers['x-amz-date'] = amzdate
201
+ headers['user-agent'] = @user_agent
202
+ else
203
+ headers['x-amzn-authorization'] = get_aws_auth_header_v2
204
+ headers['date'] = action_time.httpdate
205
+ headers['user-agent'] = @user_agent
206
+ end
207
+ headers
208
+ end
209
+
210
+ # Set the Authorization header using AWS signed header authentication
211
+ def get_aws_auth_header_v2
212
+ encoded_canonical = SES.encode(@secret_access_key, httpdate, false)
213
+ SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical)
214
+ end
215
+
216
+ def get_aws_auth_header_v4
217
+ SES.authorization_header_v4(sig_v4_auth_credential, sig_v4_auth_signed_headers, sig_v4_auth_signature)
218
+ end
219
+
220
+ private
221
+
222
+ def sig_v4_auth_credential
223
+ @access_key_id + '/' + credential_scope
224
+ end
225
+
226
+ def sig_v4_auth_signed_headers
227
+ 'host;x-amz-date'
228
+ end
229
+
230
+ def credential_scope
231
+ datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request'
232
+ end
233
+
234
+ def string_to_sign
235
+ "AWS4-HMAC-SHA256\n" + amzdate + "\n" + credential_scope + "\n" + Digest::SHA256.hexdigest(canonical_request.encode('utf-8').b)
236
+ end
237
+
238
+ def amzdate
239
+ @action_time ||= Time.now.getutc
240
+ action_time.strftime('%Y%m%dT%H%M%SZ')
241
+ end
242
+
243
+ def datestamp
244
+ @action_time ||= Time.now.getutc
245
+ action_time.strftime('%Y%m%d')
246
+ end
247
+
248
+ def httpdate
249
+ @action_time ||= Time.now.getutc.httpdate
250
+ end
251
+
252
+ def canonical_request
253
+ "POST" + "\n" + "/" + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + sig_v4_auth_signed_headers + "\n" + payload_hash
254
+ end
255
+
256
+ def canonical_querystring
257
+ signature_version == 2 ? "Action=#{action}&Version=2013-10-15" : ''
258
+ end
259
+
260
+ def canonical_headers
261
+ 'host:' + server + "\n" + 'x-amz-date:' + amzdate + "\n"
262
+ end
263
+
264
+ def payload_hash
265
+ Digest::SHA256.hexdigest(query.to_s.encode('utf-8'))
266
+ end
267
+
268
+ def sig_v4_auth_signature
269
+ OpenSSL::HMAC.hexdigest("SHA256", getSignatureKey, string_to_sign.encode('utf-8'))
270
+ end
271
+
272
+ def getSignatureKey
273
+ kDate = sign(('AWS4' + @secret_access_key).encode('utf-8'), datestamp)
274
+ kRegion = sign(kDate, region)
275
+ kService = sign(kRegion, SERVICE)
276
+ kSigning = sign(kService, 'aws4_request')
277
+
278
+ kSigning
279
+ end
280
+
281
+ def sign(key, msg)
282
+ OpenSSL::HMAC.digest("SHA256", key, msg.encode('utf-8'))
283
+ end
284
+ end # class Base
285
+ end # Module SES
286
+ end # Module AWS