Skip to content
This repository has been archived by the owner on May 4, 2021. It is now read-only.

Commit

Permalink
add changes for proper CS Auth work
Browse files Browse the repository at this point in the history
  • Loading branch information
YRafalsky committed Oct 16, 2014
1 parent 8f500ce commit 976e4c3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 15 deletions.
38 changes: 25 additions & 13 deletions boto/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def canonical_headers(self, headers_to_sign):
them into a string, separated by newlines.
"""
l = sorted(['%s:%s' % (n.lower().strip(),
headers_to_sign[n].strip()) for n in headers_to_sign])
headers_to_sign[n].strip()) for n in headers_to_sign])
return '\n'.join(l)

def string_to_sign(self, http_request):
Expand Down Expand Up @@ -305,12 +305,15 @@ def _sign(self, key, msg, hex=False):
sig = hmac.new(key, msg.encode('utf-8'), sha256).digest()
return sig

def headers_to_sign(self, http_request):
def headers_to_sign(self, http_request, override_for_cloudsearch=False):
"""
Select the headers from the request that need to be included
in the StringToSign.
"""
host_header_value = self.host_header(self.host, http_request)
if override_for_cloudsearch:
host_header_value = http_request.host
else:
host_header_value = self.host_header(self.host, http_request)
headers_to_sign = {'Host': host_header_value}
for name, value in http_request.headers.items():
lname = name.lower()
Expand Down Expand Up @@ -394,21 +397,24 @@ def payload(self, http_request):
body = body.encode('utf-8')
return sha256(body).hexdigest()

def canonical_request(self, http_request):
def canonical_request(self, http_request, override_for_cloudsearch=False):
cr = [http_request.method.upper()]
cr.append(self.canonical_uri(http_request))
cr.append(self.canonical_query_string(http_request))
headers_to_sign = self.headers_to_sign(http_request)
headers_to_sign = self.headers_to_sign(http_request, override_for_cloudsearch=override_for_cloudsearch)
cr.append(self.canonical_headers(headers_to_sign) + '\n')
cr.append(self.signed_headers(headers_to_sign))
cr.append(self.payload(http_request))
return '\n'.join(cr)

def scope(self, http_request):
def scope(self, http_request, override_for_cloudsearch=False):
scope = [self._provider.access_key]
scope.append(http_request.timestamp)
scope.append(http_request.region_name)
scope.append(http_request.service_name)
if override_for_cloudsearch:
scope.append('cloudsearch')
else:
scope.append(http_request.service_name)
scope.append('aws4_request')
return '/'.join(scope)

Expand Down Expand Up @@ -440,15 +446,18 @@ def determine_service_name(self, host):
service_name = parts[0]
return service_name

def credential_scope(self, http_request):
def credential_scope(self, http_request, override_for_cloudsearch=False):
scope = []
http_request.timestamp = http_request.headers['X-Amz-Date'][0:8]
scope.append(http_request.timestamp)
# The service_name and region_name either come from:
# * The service_name/region_name attrs or (if these values are None)
# * parsed from the endpoint <service>.<region>.amazonaws.com.
region_name = self.determine_region_name(http_request.host)
service_name = self.determine_service_name(http_request.host)
if override_for_cloudsearch:
service_name = 'cloudsearch'
else:
service_name = self.determine_service_name(http_request.host)
http_request.service_name = service_name
http_request.region_name = region_name

Expand All @@ -457,15 +466,15 @@ def credential_scope(self, http_request):
scope.append('aws4_request')
return '/'.join(scope)

def string_to_sign(self, http_request, canonical_request):
def string_to_sign(self, http_request, canonical_request, override_for_cloudsearch=False):
"""
Return the canonical StringToSign as well as a dict
containing the original version of all headers that
were included in the StringToSign.
"""
sts = ['AWS4-HMAC-SHA256']
sts.append(http_request.headers['X-Amz-Date'])
sts.append(self.credential_scope(http_request))
sts.append(self.credential_scope(http_request, override_for_cloudsearch=override_for_cloudsearch))
sts.append(sha256(canonical_request.encode('utf-8')).hexdigest())
return '\n'.join(sts)

Expand All @@ -487,6 +496,9 @@ def add_auth(self, req, **kwargs):
"""
# This could be a retry. Make sure the previous
# authorization header is removed first.
override_for_cloudsearch = False
if 'override_for_cloudsearch' in kwargs:
override_for_cloudsearch = True
if 'X-Amzn-Authorization' in req.headers:
del req.headers['X-Amzn-Authorization']
now = datetime.datetime.utcnow()
Expand All @@ -508,9 +520,9 @@ def add_auth(self, req, **kwargs):
if qs:
# Don't insert the '?' unless there's actually a query string
req.path = req.path + '?' + qs
canonical_request = self.canonical_request(req)
canonical_request = self.canonical_request(req, override_for_cloudsearch=override_for_cloudsearch)
boto.log.debug('CanonicalRequest:\n%s' % canonical_request)
string_to_sign = self.string_to_sign(req, canonical_request)
string_to_sign = self.string_to_sign(req, canonical_request, override_for_cloudsearch=override_for_cloudsearch)
boto.log.debug('StringToSign:\n%s' % string_to_sign)
signature = self.signature(req, string_to_sign)
boto.log.debug('Signature:\n%s' % signature)
Expand Down
3 changes: 2 additions & 1 deletion boto/cloudsearch2/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ def commit(self):
)
session.mount('http://', adapter)
session.mount('https://', adapter)
r2 = self.domain.layer1.make_dummy_request_for_cloudsearch2( action=None, path='/'+api_version+'/documents/batch', verb='POST', host=self.endpoint,headers={'Content-Type': 'application/json'}, data=sdf)
r = session.post(url, data=sdf,
headers={'Content-Type': 'application/json'})
headers=r2.headers)

return CommitResponse(r, self, sdf)

Expand Down
9 changes: 8 additions & 1 deletion boto/cloudsearch2/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,9 @@ def __call__(self, query):
url = "http://%s/%s/search" % (self.endpoint, api_version)
params = query.to_params()

r = self.session.get(url, params=params)
r2 = self.domain.layer1.make_dummy_request_for_cloudsearch2( action=None, params=params, path='/'+api_version+'/search', verb='GET', host=self.endpoint)
r = self.session.get(url, params=params, headers = r2.headers)

_body = r.content.decode('utf-8')
try:
data = json.loads(_body)
Expand All @@ -294,6 +296,11 @@ def __call__(self, query):
raise SearchServiceException('Authentication error from Amazon%s' % msg)
raise SearchServiceException("Got non-json response from Amazon. %s" % _body, query)

if '__type' in data and data['__type']=='#AccessDenied':
message = 'Access Denied'
if 'message' in data:
message = data['message']
raise SearchServiceException(message, '#AccessDenied')
if 'messages' in data and 'error' in data:
for m in data['messages']:
if m['severity'] == 'fatal':
Expand Down
11 changes: 11 additions & 0 deletions boto/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,17 @@ def make_request(self, action, params=None, path='/', verb='GET'):
http_request.params['Version'] = self.APIVersion
return self._mexe(http_request)

def make_dummy_request_for_cloudsearch2(self, action, params=None, path='/', verb='GET', host = None, data='', headers={}):
http_request = self.build_base_http_request(verb, path, None,
params, headers, data,
host)
self._auth_handler.add_auth(req=http_request, override_for_cloudsearch=True)
if action:
http_request.params['Action'] = action
if self.APIVersion:
http_request.params['Version'] = self.APIVersion
return http_request

def build_list_params(self, params, items, label):
if isinstance(items, six.string_types):
items = [items]
Expand Down

0 comments on commit 976e4c3

Please sign in to comment.