class AWS::SES::Base

AWS::SES::Base is the abstract super class of all classes who make requests against SES

Attributes

message_id_domain[R]
port[R]
proxy_server[R]
region[R]
server[R]
settings[RW]
signature_version[R]
use_ssl[R]

Public Class Methods

new( options = {} ) click to toggle source

@option options [String] :access_key_id (“”) The user's AWS Access Key ID @option options [String] :secret_access_key (“”) The user's AWS Secret Access Key @option options [Boolean] :use_ssl (true) Connect using SSL? @option options [String] :server (“email.us-east-1.amazonaws.com”) The server API endpoint host @option options [String] :proxy_server (nil) An HTTP proxy server FQDN @option options [String] :user_agent (“github-aws-ses-ruby-gem”) The HTTP User-Agent header value @option options [String] :region (“us-east-1”) The server API endpoint host @option options [String] :message_id_domain (“us-east-1.amazonses.com”) Domain used to build message_id header @return [Object] the object.

# File lib/aws/ses/base.rb, line 100
def initialize( options = {} )

  options = { :access_key_id => "",
              :secret_access_key => "",
              :use_ssl => true,
              :server => DEFAULT_HOST,
              :message_id_domain => DEFAULT_MESSAGE_ID_DOMAIN,
              :path => "/",
              :user_agent => USER_AGENT,
              :proxy_server => nil,
              :region => DEFAULT_REGION
              }.merge(options)

  @signature_version = options[:signature_version] || 2
  @server = options[:server]
  @message_id_domain = options[:message_id_domain]
  @proxy_server = options[:proxy_server]
  @use_ssl = options[:use_ssl]
  @path = options[:path]
  @user_agent = options[:user_agent]
  @region = options[:region]
  @settings = {}

  raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
  raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
  raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
  raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
  raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?

  if options[:port]
    # user-specified port
    @port = options[:port]
  elsif @use_ssl
    # https
    @port = 443
  else
    # http
    @port = 80
  end

  @access_key_id = options[:access_key_id]
  @secret_access_key = options[:secret_access_key]

  # Use proxy server if defined
  # Based on patch by Mathias Dalheimer.  20070217
  proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
  @http = Net::HTTP::Proxy( proxy.host,
                            proxy.port,
                            proxy.user,
                            proxy.password).new(options[:server], @port)

  @http.use_ssl = @use_ssl
end

Public Instance Methods

addresses() click to toggle source
# File lib/aws/ses/addresses.rb, line 69
def addresses
  @addresses ||= Addresses.new(self)
end
connection() click to toggle source
# File lib/aws/ses/base.rb, line 154
def connection
  @http
end
get_aws_auth_param(timestamp, secret_access_key, action = '', signature_version = 2) click to toggle source

Set the Authorization header using AWS signed header authentication

# File lib/aws/ses/base.rb, line 197
def get_aws_auth_param(timestamp, secret_access_key, action = '', signature_version = 2)
  raise(ArgumentError, "signature_version must be `2` or `4`") unless signature_version == 2 || signature_version == 4
  encoded_canonical = SES.encode(secret_access_key, timestamp, false)

  if signature_version == 4
    SES.authorization_header_v4(sig_v4_auth_credential, sig_v4_auth_signed_headers, sig_v4_auth_signature(action))
  else
    SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical)
  end
end
request(action, params = {}) click to toggle source

Make the connection to AWS passing in our request.

allow us to have a one line call in each method which will do all of the work in making the actual request to AWS.

# File lib/aws/ses/base.rb, line 161
def request(action, params = {})
  # Use a copy so that we don't modify the caller's Hash, remove any keys that have nil or empty values
  params = params.reject { |key, value| value.nil? or value.empty?}
  
  timestamp = Time.now.getutc

  params.merge!( {"Action" => action,
                  "SignatureVersion" => signature_version.to_s,
                  "SignatureMethod" => 'HmacSHA256',
                  "AWSAccessKeyId" => @access_key_id,
                  "Version" => API_VERSION,
                  "Timestamp" => timestamp.iso8601 } )

  query = params.sort.collect do |param|
    CGI::escape(param[0]) + "=" + CGI::escape(param[1])
  end.join("&")

  req = {}

  req['X-Amzn-Authorization'] = get_aws_auth_param(timestamp.httpdate, @secret_access_key, action, signature_version)
  req['Date'] = timestamp.httpdate
  req['User-Agent'] = @user_agent

  response = connection.post(@path, query, req)
  
  response_class = AWS::SES.const_get( "#{action}Response" )
  result = response_class.new(action, response)
  
  if result.error?
    raise ResponseError.new(result)
  end
  
  result
end

Private Instance Methods

amzdate() click to toggle source
# File lib/aws/ses/base.rb, line 227
def amzdate
  Time.now.utc.strftime('%Y%m%dT%H%M%SZ')
end
canonical_headers() click to toggle source
# File lib/aws/ses/base.rb, line 243
def canonical_headers
  'host:' + server + "\n" + 'x-amz-date:' + amzdate + "\n"
end
canonical_querystring(action) click to toggle source
# File lib/aws/ses/base.rb, line 239
def canonical_querystring(action)
  "Action=#{action}&Version=2013-10-15"
end
canonical_request(for_action) click to toggle source
# File lib/aws/ses/base.rb, line 235
def canonical_request(for_action)
  "GET" + "\n" + "/" + "\n" + canonical_querystring(for_action) + "\n" + canonical_headers + "\n" + sig_v4_auth_signed_headers + "\n" + payload_hash
end
credential_scope() click to toggle source
# File lib/aws/ses/base.rb, line 218
def credential_scope
  datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request'
end
datestamp() click to toggle source
# File lib/aws/ses/base.rb, line 231
def datestamp
  Time.now.utc.strftime('%Y%m%d')
end
getSignatureKey(key, dateStamp, regionName, serviceName) click to toggle source
# File lib/aws/ses/base.rb, line 257
def getSignatureKey(key, dateStamp, regionName, serviceName)
  kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
  kRegion = sign(kDate, regionName)
  kService = sign(kRegion, serviceName)
  kSigning = sign(kService, 'aws4_request')

  kSigning
end
payload_hash() click to toggle source
# File lib/aws/ses/base.rb, line 247
def payload_hash
  Digest::SHA256.hexdigest(''.encode('utf-8'))
end
sig_v4_auth_credential() click to toggle source
# File lib/aws/ses/base.rb, line 210
def sig_v4_auth_credential
  @access_key_id + '/' + credential_scope
end
sig_v4_auth_signature(for_action) click to toggle source
# File lib/aws/ses/base.rb, line 251
def sig_v4_auth_signature(for_action)
  signing_key = getSignatureKey(@secret_access_key, datestamp, region, SERVICE)

  OpenSSL::HMAC.hexdigest("SHA256", signing_key, string_to_sign(for_action).encode('utf-8'))
end
sig_v4_auth_signed_headers() click to toggle source
# File lib/aws/ses/base.rb, line 214
def sig_v4_auth_signed_headers
  'host;x-amz-date'
end
sign(key, msg) click to toggle source
# File lib/aws/ses/base.rb, line 266
def sign(key, msg)
  OpenSSL::HMAC.digest("SHA256", key, msg.encode('utf-8'))
end
string_to_sign(for_action) click to toggle source
# File lib/aws/ses/base.rb, line 222
def string_to_sign(for_action)
  "AWS4-HMAC-SHA256\n" +  amzdate + "\n" +  credential_scope + "\n" + Digest::SHA256.hexdigest(canonical_request(for_action).encode('utf-8').b)
end