class AWS::SES::Base

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

Attributes

action[R]
action_time[R]
message_id_domain[R]
port[R]
proxy_server[R]
query[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 103
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,
              :signature_version => DEFAULT_SIGNATURE_VERSION
  }.merge(options)

  @signature_version = options[:signature_version]
  @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?
  raise ArgumentError, ":signature_version must be 2 or 4" unless [2, 4].include?(options[:signature_version])

  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 159
def connection
  @http
end
get_aws_auth_header_v2() click to toggle source

Set the Authorization header using AWS signed header authentication

# File lib/aws/ses/base.rb, line 211
def get_aws_auth_header_v2
  encoded_canonical = SES.encode(@secret_access_key, httpdate, false)
  SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical)
end
get_aws_auth_header_v4() click to toggle source
# File lib/aws/ses/base.rb, line 216
def get_aws_auth_header_v4
  SES.authorization_header_v4(sig_v4_auth_credential, sig_v4_auth_signed_headers, sig_v4_auth_signature)
end
get_req_headers() click to toggle source
# File lib/aws/ses/base.rb, line 195
def get_req_headers
  headers = {}
  if signature_version == 4
    headers['host'] = @server
    headers['authorization'] = get_aws_auth_header_v4
    headers['x-amz-date'] = amzdate
    headers['user-agent'] = @user_agent
  else
    headers['x-amzn-authorization'] = get_aws_auth_header_v2
    headers['date'] = action_time.httpdate
    headers['user-agent'] = @user_agent
  end
  headers
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 166
def request(action, params = {})
  @action = action
  # 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 { |_, value| value.nil? or value.empty?}

  @action_time = Time.now.getutc

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

  @query = params.sort.collect do |param|
    CGI::escape(param[0]) + "=" + CGI::escape(param[1])
  end.join("&")
  response = connection.post(@path, query, get_req_headers)

  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 238
def amzdate
  @action_time ||= Time.now.getutc
  action_time.strftime('%Y%m%dT%H%M%SZ')
end
canonical_headers() click to toggle source
# File lib/aws/ses/base.rb, line 260
def canonical_headers
  'host:' + server + "\n" + 'x-amz-date:' + amzdate + "\n"
end
canonical_querystring() click to toggle source
# File lib/aws/ses/base.rb, line 256
def canonical_querystring
  signature_version == 2 ? "Action=#{action}&Version=2013-10-15" : ''
end
canonical_request() click to toggle source
# File lib/aws/ses/base.rb, line 252
def canonical_request
  "POST" + "\n" + "/" + "\n" + canonical_querystring + "\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 230
def credential_scope
  datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request'
end
datestamp() click to toggle source
# File lib/aws/ses/base.rb, line 243
def datestamp
  @action_time ||= Time.now.getutc
  action_time.strftime('%Y%m%d')
end
getSignatureKey() click to toggle source
# File lib/aws/ses/base.rb, line 272
def getSignatureKey
  kDate = sign(('AWS4' + @secret_access_key).encode('utf-8'), datestamp)
  kRegion = sign(kDate, region)
  kService = sign(kRegion, SERVICE)
  kSigning = sign(kService, 'aws4_request')

  kSigning
end
httpdate() click to toggle source
# File lib/aws/ses/base.rb, line 248
def httpdate
  @action_time ||= Time.now.getutc.httpdate
end
payload_hash() click to toggle source
# File lib/aws/ses/base.rb, line 264
def payload_hash
  Digest::SHA256.hexdigest(query.to_s.encode('utf-8'))
end
sig_v4_auth_credential() click to toggle source
# File lib/aws/ses/base.rb, line 222
def sig_v4_auth_credential
  @access_key_id + '/' + credential_scope
end
sig_v4_auth_signature() click to toggle source
# File lib/aws/ses/base.rb, line 268
def sig_v4_auth_signature
  OpenSSL::HMAC.hexdigest("SHA256", getSignatureKey, string_to_sign.encode('utf-8'))
end
sig_v4_auth_signed_headers() click to toggle source
# File lib/aws/ses/base.rb, line 226
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 281
def sign(key, msg)
  OpenSSL::HMAC.digest("SHA256", key, msg.encode('utf-8'))
end
string_to_sign() click to toggle source
# File lib/aws/ses/base.rb, line 234
def string_to_sign
  "AWS4-HMAC-SHA256\n" +  amzdate + "\n" +  credential_scope + "\n" + Digest::SHA256.hexdigest(canonical_request.encode('utf-8').b)
end