Commits

Anonymous committed 9a86ebf Merge

Added Request Authentication

Amazon requires to authenticate your requests beginning August 15, 2009.

Set your Secret Message with ecs.setSecretAccessKey('12345')

Comments (0)

Files changed (2)

 
 - `AWSException`
 - `NoLicenseKey`
+- `NoSecretAccessKey`
 - `BadLocale`
 - `BadOption`
 - `ExactParameterRequirement`
    c) setup the locale if your locale is not ``us``
 
 4. Send query to the AWS, and manupilate the returned python object.
+
 """
 
 __author__ = "Kun Xi < kunxi@kunxi.org >"
 __docformat__ = 'restructuredtext'
 
 
-import os, urllib, string
+import os, urllib, string, hmac, hashlib
+from datetime import datetime
 from xml.dom import minidom
 
 
 # Package-wide variables:
-LICENSE_KEY = None;
+LICENSE_KEY = None
+SECRET_ACCESS_KEY = None
 LOCALE = "us"
 VERSION = "2007-04-04"
 OPTIONS = {}
     (lambda key: os.environ.get('AWS_LICENSE_KEY', None))
    )
 
+__secretAccessKeys = (
+    (lambda key: key),
+    (lambda key: SECRET_ACCESS_KEY), 
+    (lambda key: os.environ.get('AWS_SECRET_ACCESS_KEY', None))
+   )
+
 
 
 def __buildPlugins():
 # Exception classes
 class AWSException(Exception) : pass
 class NoLicenseKey(AWSException) : pass
+class NoSecretAccessKey(AWSException) : pass
 class BadLocale(AWSException) : pass
 class BadOption(AWSException): pass
 # Runtime exception
         setLicenseKey()
     return LICENSE_KEY
 
+def setSecretAccessKey(secret_access_key=None):
+    """Sets your secret AWS key.
+    If secret_access_key is not specified, we look for the 
+    environment variable: AMAZON_SECRET_ACCESS_KEY.  
+    Raises NoSecretAccessKey if we can't get it to work."""
+    
+    global SECRET_ACCESS_KEY
+    for get in __secretAccessKeys:
+        rc = get(secret_access_key)
+        if rc: 
+            SECRET_ACCESS_KEY = rc;
+            return;
+    raise NoSecretAccessKey, ("Please get your secret key from  http://www.amazon.com/webservices")
+
+def getSecretAccessKey():
+    """Get the secret access key.
+    If no key is specified,  NoSecretAccessKey is raised."""
+
+    if not SECRET_ACCESS_KEY:
+        setSecretAccessKey()
+    return SECRET_ACCESS_KEY
 
 def getVersion():
     """Get the version of ECS specification"""
     """Get options"""
     return OPTIONS 
 
+def buildSignature(netloc,query_string):
+    secret_key = getSecretAccessKey()
+    string_to_sign = 'GET\n%s\n%s\n%s' % (netloc,'/onca/xml',query_string)
+    
+    return urllib.quote_plus(hmac.new(secret_key,string_to_sign,hashlib.sha256).digest().encode('base64').strip())
+
+def buildQuery(argv):
+    # 1. Filter any key set to 'None'
+    # 2. Sort the dict by key
+    # 3. Quote everything and build the query string
+    query_string = urllib.urlencode([(k, argv[k]) for (k) in sorted(argv.keys()) if argv[k]])
+
+    netloc = __supportedLocales[getLocale()]
+    signature = buildSignature(netloc, query_string)
+    url = 'http://' + netloc + '/onca/xml?' + query_string + '&Signature=' + signature
+    return url
 
 def buildRequest(argv):
-    """Build the REST request URL from argv."""
+    """Adds some standard keys (like Timestamp and Version) to the request,
+    then builds and returns the request-url."""
 
-    url = "http://" + __supportedLocales[getLocale()] + "/onca/xml?Service=AWSECommerceService&" + 'Version=%s&' % VERSION
     if not argv['AWSAccessKeyId']:
         argv['AWSAccessKeyId'] = getLicenseKey()
     argv.update(getOptions())
-    return url + '&'.join(['%s=%s' % (k,urllib.quote(str(v))) for (k,v) in argv.items() if v]) 
+    argv.update({'Service':'AWSECommerceService',
+                 'Timestamp':datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'),
+                 'Version':VERSION})
 
+    return buildQuery(argv)
 
 def buildException(els):
     """Build the exception from the returned DOM node

tests/authentication.py

+import unittest
+import sys
+
+# quick-n-dirty for debug only
+sys.path.append('..')
+import ecs
+
+class SettingYourSecretAccessKeyTest( unittest.TestCase ):
+    
+    def setUp(self):
+        ecs.SECRET_ACCESS_KEY = None
+
+    def testGetSecretAccessKey(self):
+        self.assertRaises( ecs.NoSecretAccessKey, ecs.getSecretAccessKey )
+
+    def testSetSecretAccessKey(self):
+        self.assertRaises( ecs.NoSecretAccessKey, ecs.setSecretAccessKey )
+
+    def testSetSecretKeyFromEnv(self):
+        import os
+        os.environ['AWS_SECRET_ACCESS_KEY'] = "FAKE-KEY"
+        self.assertEqual( ecs.getSecretAccessKey(), "FAKE-KEY" )
+        
+    def testSetSecretKey(self):
+        ecs.setSecretAccessKey('1234')
+        self.assertEqual(ecs.getSecretAccessKey(), '1234')
+        
+class AuthenticationTest(unittest.TestCase):
+    def testBuildTheSignature(self):
+        '''example taken from the api-documentation'''
+        netloc = 'webservices.amazon.com'
+        query = 'AWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06'
+        expected_signature = 'Nace%2BU3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg%3D'
+        self.assertEqual(ecs.buildSignature(netloc, query),expected_signature)
+                
+    def testBuildQuery(self):
+        '''example taken from the api-documentation'''
+        ecs.setSecretAccessKey('1234567890')
+        
+        args = {
+                'Service':'AWSECommerceService',
+                'AWSAccessKeyId':'00000000000000000000',
+                'Timestamp':'2009-01-01T12:00:00Z',
+                'Operation':'CartCreate',
+                'Item.1.OfferListingId':'j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5j+e8MS449BN1XGUC3DfU5Zw4nt/FBt87cspLow1QXzfvZpvzg==',
+                'Item.1.Quantity':'3',
+                'AssociateTag':'mytag-20',
+                'Version':'2009-01-01'
+        }
+        expepected_url = 'http://ecs.amazonaws.com/onca/xml?AWSAccessKeyId=00000000000000000000&AssociateTag=mytag-20&Item.1.OfferListingId=j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5j%2Be8MS449BN1XGUC3DfU5Zw4nt%2FFBt87cspLow1QXzfvZpvzg%3D%3D&Item.1.Quantity=3&Operation=CartCreate&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01&Signature=cF3UtjbJb1%2BxDh387C%2FEmS1BCtS%2FZ01taykBCGemvUU%3D'
+        self.assertEqual(ecs.buildQuery(args),expepected_url)
+
+if __name__ == "__main__" :
+    unittest.main()
+