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:
@@ -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
|
||||||
|
@@ -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):
|
||||||
|
Reference in New Issue
Block a user