| Index: third_party/boto/boto/auth.py
|
| ===================================================================
|
| --- third_party/boto/boto/auth.py (revision 33376)
|
| +++ third_party/boto/boto/auth.py (working copy)
|
| @@ -36,6 +36,7 @@
|
| import datetime
|
| from email.utils import formatdate
|
| import hmac
|
| +import os
|
| import sys
|
| import time
|
| import urllib
|
| @@ -220,7 +221,6 @@
|
| Select the headers from the request that need to be included
|
| in the StringToSign.
|
| """
|
| - headers_to_sign = {}
|
| headers_to_sign = {'Host': self.host}
|
| for name, value in http_request.headers.items():
|
| lname = name.lower()
|
| @@ -329,7 +329,7 @@
|
| parameter_names = sorted(http_request.params.keys())
|
| pairs = []
|
| for pname in parameter_names:
|
| - pval = str(http_request.params[pname]).encode('utf-8')
|
| + pval = boto.utils.get_utf8_value(http_request.params[pname])
|
| pairs.append(urllib.quote(pname, safe='') + '=' +
|
| urllib.quote(pval, safe='-_~'))
|
| return '&'.join(pairs)
|
| @@ -341,7 +341,7 @@
|
| return ""
|
| l = []
|
| for param in sorted(http_request.params):
|
| - value = str(http_request.params[param])
|
| + value = boto.utils.get_utf8_value(http_request.params[param])
|
| l.append('%s=%s' % (urllib.quote(param, safe='-_.~'),
|
| urllib.quote(value, safe='-_.~')))
|
| return '&'.join(l)
|
| @@ -358,9 +358,11 @@
|
| for header in headers_to_sign:
|
| c_name = header.lower().strip()
|
| raw_value = headers_to_sign[header]
|
| - c_value = ' '.join(raw_value.strip().split())
|
| + if '"' in raw_value:
|
| + c_value = raw_value.strip()
|
| + else:
|
| + c_value = ' '.join(raw_value.strip().split())
|
| canonical.append('%s:%s' % (c_name, c_value))
|
| -
|
| return '\n'.join(sorted(canonical))
|
|
|
| def signed_headers(self, headers_to_sign):
|
| @@ -498,7 +500,10 @@
|
| # Safe to modify req.path here since
|
| # the signature will use req.auth_path.
|
| req.path = req.path.split('?')[0]
|
| - req.path = req.path + '?' + qs
|
| +
|
| + if qs:
|
| + # Don't insert the '?' unless there's actually a query string
|
| + req.path = req.path + '?' + qs
|
| canonical_request = self.canonical_request(req)
|
| boto.log.debug('CanonicalRequest:\n%s' % canonical_request)
|
| string_to_sign = self.string_to_sign(req, canonical_request)
|
| @@ -597,6 +602,11 @@
|
| if part == 's3':
|
| # If it's by itself, the region is the previous part.
|
| region_name = parts[-offset]
|
| +
|
| + # Unless it's Vhosted classic
|
| + if region_name == 'amazonaws':
|
| + region_name = 'us-east-1'
|
| +
|
| break
|
| elif part.startswith('s3-'):
|
| region_name = self.clean_region_name(part)
|
| @@ -661,7 +671,55 @@
|
| req = self.mangle_path_and_params(req)
|
| return super(S3HmacAuthV4Handler, self).add_auth(req, **kwargs)
|
|
|
| + def presign(self, req, expires, iso_date=None):
|
| + """
|
| + Presign a request using SigV4 query params. Takes in an HTTP request
|
| + and an expiration time in seconds and returns a URL.
|
|
|
| + http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
|
| + """
|
| + if iso_date is None:
|
| + iso_date = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
|
| +
|
| + region = self.determine_region_name(req.host)
|
| + service = self.determine_service_name(req.host)
|
| +
|
| + params = {
|
| + 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
|
| + 'X-Amz-Credential': '%s/%s/%s/%s/aws4_request' % (
|
| + self._provider.access_key,
|
| + iso_date[:8],
|
| + region,
|
| + service
|
| + ),
|
| + 'X-Amz-Date': iso_date,
|
| + 'X-Amz-Expires': expires,
|
| + 'X-Amz-SignedHeaders': 'host'
|
| + }
|
| +
|
| + if self._provider.security_token:
|
| + params['X-Amz-Security-Token'] = self._provider.security_token
|
| +
|
| + req.params.update(params)
|
| +
|
| + cr = self.canonical_request(req)
|
| +
|
| + # We need to replace the payload SHA with a constant
|
| + cr = '\n'.join(cr.split('\n')[:-1]) + '\nUNSIGNED-PAYLOAD'
|
| +
|
| + # Date header is expected for string_to_sign, but unused otherwise
|
| + req.headers['X-Amz-Date'] = iso_date
|
| +
|
| + sts = self.string_to_sign(req, cr)
|
| + signature = self.signature(req, sts)
|
| +
|
| + # Add signature to params now that we have it
|
| + req.params['X-Amz-Signature'] = signature
|
| +
|
| + return 'https://%s%s?%s' % (req.host, req.path,
|
| + urllib.urlencode(req.params))
|
| +
|
| +
|
| class QueryAuthHandler(AuthHandler):
|
| """
|
| Provides pure query construction (no actual signing).
|
| @@ -892,7 +950,16 @@
|
|
|
| def detect_potential_sigv4(func):
|
| def _wrapper(self):
|
| + if os.environ.get('EC2_USE_SIGV4', False):
|
| + return ['hmac-v4']
|
| +
|
| + if boto.config.get('ec2', 'use-sigv4', False):
|
| + return ['hmac-v4']
|
| +
|
| if hasattr(self, 'region'):
|
| + # If you're making changes here, you should also check
|
| + # ``boto/iam/connection.py``, as several things there are also
|
| + # endpoint-related.
|
| if getattr(self.region, 'endpoint', ''):
|
| if '.cn-' in self.region.endpoint:
|
| return ['hmac-v4']
|
| @@ -903,7 +970,16 @@
|
|
|
| def detect_potential_s3sigv4(func):
|
| def _wrapper(self):
|
| + if os.environ.get('S3_USE_SIGV4', False):
|
| + return ['hmac-v4-s3']
|
| +
|
| + if boto.config.get('s3', 'use-sigv4', False):
|
| + return ['hmac-v4-s3']
|
| +
|
| if hasattr(self, 'host'):
|
| + # If you're making changes here, you should also check
|
| + # ``boto/iam/connection.py``, as several things there are also
|
| + # endpoint-related.
|
| if '.cn-' in self.host:
|
| return ['hmac-v4-s3']
|
|
|
|
|