Use requests library in S3 middleware

Convert the S3 token middleware to use the requests library. It does not
validate certificates as part of this patch to keep new functionality
isolated.

Change-Id: I0f26c80a969b919de80410af0a80920bd8493191
Closes-Bug: #1275598
This commit is contained in:
Jamie Lennox
2014-02-03 13:14:53 +10:00
parent 7736825235
commit 9fda5add29
2 changed files with 52 additions and 33 deletions

View File

@@ -33,11 +33,12 @@ This WSGI component:
""" """
import httplib
import logging import logging
import urllib import urllib
import webob import webob
import requests
from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import jsonutils
@@ -105,16 +106,26 @@ class S3Token(object):
self.logger.debug('Starting the %s component' % PROTOCOL_NAME) self.logger.debug('Starting the %s component' % PROTOCOL_NAME)
self.reseller_prefix = conf.get('reseller_prefix', 'AUTH_') self.reseller_prefix = conf.get('reseller_prefix', 'AUTH_')
# where to find the auth service (we use this to validate tokens) # where to find the auth service (we use this to validate tokens)
self.auth_host = conf.get('auth_host')
self.auth_port = int(conf.get('auth_port', 35357)) auth_host = conf.get('auth_host')
self.auth_protocol = conf.get('auth_protocol', 'https') auth_port = int(conf.get('auth_port', 35357))
if self.auth_protocol == 'http': auth_protocol = conf.get('auth_protocol', 'https')
self.http_client_class = httplib.HTTPConnection
else: self.request_uri = '%s://%s:%s' % (auth_protocol, auth_host, auth_port)
self.http_client_class = httplib.HTTPSConnection
# SSL # SSL
self.cert_file = conf.get('certfile') insecure = conf.get('insecure', False)
self.key_file = conf.get('keyfile') cert_file = conf.get('certfile')
key_file = conf.get('keyfile')
if insecure:
self.verify = False
elif cert_file and key_file:
self.verify = (cert_file, key_file)
elif cert_file:
self.verify = cert_file
else:
self.verify = None
def deny_request(self, code): def deny_request(self, code):
error_table = { error_table = {
@@ -132,33 +143,22 @@ class S3Token(object):
def _json_request(self, creds_json): def _json_request(self, creds_json):
headers = {'Content-Type': 'application/json'} headers = {'Content-Type': 'application/json'}
if self.auth_protocol == 'http':
conn = self.http_client_class(self.auth_host, self.auth_port)
else:
conn = self.http_client_class(self.auth_host,
self.auth_port,
self.key_file,
self.cert_file)
try: try:
conn.request('POST', '/v2.0/s3tokens', response = requests.post('%s/v2.0/s3tokens' % self.request_uri,
body=creds_json, headers=headers, data=creds_json,
headers=headers) verify=self.verify)
response = conn.getresponse() except requests.exceptions.RequestException as e:
output = response.read()
except Exception as e:
self.logger.info('HTTP connection exception: %s' % e) self.logger.info('HTTP connection exception: %s' % e)
resp = self.deny_request('InvalidURI') resp = self.deny_request('InvalidURI')
raise ServiceError(resp) raise ServiceError(resp)
finally:
conn.close()
if response.status < 200 or response.status >= 300: if response.status_code < 200 or response.status_code >= 300:
self.logger.debug('Keystone reply error: status=%s reason=%s' % self.logger.debug('Keystone reply error: status=%s reason=%s' %
(response.status, response.reason)) (response.status_code, response.reason))
resp = self.deny_request('AccessDenied') resp = self.deny_request('AccessDenied')
raise ServiceError(resp) raise ServiceError(resp)
return (response, output) return response
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
"""Handle incoming request. authenticate and send downstream.""" """Handle incoming request. authenticate and send downstream."""
@@ -225,23 +225,23 @@ class S3Token(object):
# identified and not doing a second query and just # identified and not doing a second query and just
# pass it through to swiftauth in this case. # pass it through to swiftauth in this case.
try: try:
resp, output = self._json_request(creds_json) resp = self._json_request(creds_json)
except ServiceError as e: except ServiceError as e:
resp = e.args[0] resp = e.args[0]
msg = 'Received error, exiting middleware with error: %s' msg = 'Received error, exiting middleware with error: %s'
self.logger.debug(msg % (resp.status)) self.logger.debug(msg % (resp.status_code))
return resp(environ, start_response) return resp(environ, start_response)
self.logger.debug('Keystone Reply: Status: %d, Output: %s' % ( self.logger.debug('Keystone Reply: Status: %d, Output: %s' % (
resp.status, output)) resp.status_code, resp.content))
try: try:
identity_info = jsonutils.loads(output) identity_info = resp.json()
token_id = str(identity_info['access']['token']['id']) token_id = str(identity_info['access']['token']['id'])
tenant = identity_info['access']['token']['tenant'] tenant = identity_info['access']['token']['tenant']
except (ValueError, KeyError): except (ValueError, KeyError):
error = 'Error on keystone reply: %d %s' error = 'Error on keystone reply: %d %s'
self.logger.debug(error % (resp.status, str(output))) self.logger.debug(error % (resp.status_code, str(resp.content)))
return self.deny_request('InvalidURI')(environ, start_response) return self.deny_request('InvalidURI')(environ, start_response)
req.headers['X-Auth-Token'] = token_id req.headers['X-Auth-Token'] = token_id

View File

@@ -16,6 +16,7 @@
import httpretty import httpretty
import mock import mock
import requests
import testtools import testtools
import webob import webob
@@ -120,6 +121,24 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase):
path = req.environ['PATH_INFO'] path = req.environ['PATH_INFO']
self.assertTrue(path.startswith('/v1/AUTH_FORCED_TENANT_ID')) self.assertTrue(path.startswith('/v1/AUTH_FORCED_TENANT_ID'))
@mock.patch.object(requests, 'post')
def test_insecure(self, MOCK_REQUEST):
self.middleware = (
s3_token.filter_factory({'insecure': True})(FakeApp()))
MOCK_REQUEST.return_value = utils.TestResponse({
'status_code': 201,
'text': jsonutils.dumps(GOOD_RESPONSE)})
req = webob.Request.blank('/v1/AUTH_cfa/c/o')
req.headers['Authorization'] = 'access:signature'
req.headers['X-Storage-Token'] = 'token'
req.get_response(self.middleware)
self.assertTrue(MOCK_REQUEST.called)
mock_args, mock_kwargs = MOCK_REQUEST.call_args
self.assertIs(mock_kwargs['verify'], False)
class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase): class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase):
def setUp(self): def setUp(self):