Änderungen an der Amazon Schnittstelle

Anfang August hat Amazon seine API  entscheidend umgestellt – alle Zugriffe müssen mit dem API-Secret auf relativ komplizierte Art und Weise signiert werden. Ich nutzte bisher das Plugin “amazon-aws”, welches diese Signierung aber leider nicht unterstützt.

Da der Amazonproduktdatenimport in meinen Projekten also seit August nicht mehr funktioniert hatte entschloss ich mich dazu das Plugin entsprechend zu erweitern und auf github zur Verfügung zu stellen.

Änderungen am Plugin:

Die erste Änderung betrifft die Datei /lib/amazon/aws.rb dieser habe ich im Kopfbereich folgendes Modul hinzugefügt welches für die spätere Verschlüsselung der Signatur zuständig ist:

module HMAC
IPAD = "\x36" * 64
OPAD = "\x5c" * 64
 
module_function
 
def sha256( key, message )
ikey = IPAD.dup
okey = OPAD.dup
key.size.times do |i|
ikey[i] = key[i] ^ IPAD[i]
okey[i] = key[i] ^ OPAD[i]
end
 
value = Digest::SHA256.digest( ikey + message )
value = Digest::SHA256.digest( okey + value )
end
end

Als nächstes wurde dem Modul AWS eine Metode hinzugefügt, welche die eigentliche Signatur erstellt:

def self.signature_for_request(request, query, method = 'GET' )
      endpoint = ENDPOINT[request.locale]
      host = endpoint.host
      uri = endpoint.path
      query.gsub!('?', '')
      raw_signature = "#{method}\n#{host}\n#{uri}\n#{query}"
      hash = HMAC::sha256(request.secret_id, raw_signature)
      signature = Base64.encode64(hash).chomp
      Amazon.rawurlencode signature
    end

Zuletzt habe ich in der AWS::get_page methode die Signatur an die URL gehängt:

url = ENDPOINT[request.locale].path + query + "&Signature=" + AWS.signature_for_request(request, query)

In der Datei lib/amazon/aws/search.rb habe ich in Search::Request den intializer etwas erweitert, schließlich muss ja der SECRET-KEY mittlerweile auch übergeben werden:

 def initialize(key_id=nil, secret_id=nil, associate=nil, locale=nil, cache=nil, cache_dir=nil, user_agent=USER_AGENT)
 
    @config ||= Amazon::Config.new
 
    def_locale = locale
    locale = 'us' unless locale
    locale.downcase!
 
    key_id ||= @config['key_id']
    secret_id ||= @config['secret_id']
    cache = @config['cache'] if cache.nil?
    cache_dir ||= @config['cache_dir']
 
    # Take locale from config file if no locale was passed to method.
    #
    if @config.key?( 'locale' ) && ! def_locale
      locale = @config['locale']
    end
    validate_locale( locale )
 
    if key_id.nil?
      raise AccessKeyIdError, 'key_id may not be nil'
    end
 
    if secret_id.nil?
      raise AccessKeyIdError, 'secrret_id may not be nil'
    end
 
    @key_id = key_id
    @secret_id = secret_id
    @tag = associate || @config['associate'] || DEF_ASSOC[locale]
    @user_agent = user_agent
    @cache = unless cache == 'false' || cache == false
      Amazon::AWS::Cache.new( cache_dir )
    else
      nil
    end
    self.locale = locale
  end

Bei allen Search::Requests übergeben wir jetzt also auch den SECRETKEY und schon läuft das Ganze wieder – so lange bis amazon wieder irgendwas ändert ;)

Zum überarbeiteten Plugin bei github