Merge "Move common request processing to base class"
This commit is contained in:
commit
9db204e5c2
@ -457,6 +457,38 @@ class _BaseAuthProtocol(object):
|
||||
|
||||
By default this method does not return a value.
|
||||
"""
|
||||
request.remove_auth_headers()
|
||||
|
||||
user_auth_ref = None
|
||||
serv_auth_ref = None
|
||||
|
||||
if request.user_token:
|
||||
self.log.debug('Authenticating user token')
|
||||
try:
|
||||
data, user_auth_ref = self._do_fetch_token(request.user_token)
|
||||
self._validate_token(user_auth_ref)
|
||||
self._confirm_token_bind(user_auth_ref, request)
|
||||
except exc.InvalidToken:
|
||||
self.log.info(_LI('Invalid user token'))
|
||||
request.user_token_valid = False
|
||||
else:
|
||||
request.user_token_valid = True
|
||||
request.environ['keystone.token_info'] = data
|
||||
|
||||
if request.service_token:
|
||||
self.log.debug('Authenticating service token')
|
||||
try:
|
||||
_, serv_auth_ref = self._do_fetch_token(request.service_token)
|
||||
self._validate_token(serv_auth_ref)
|
||||
self._confirm_token_bind(serv_auth_ref, request)
|
||||
except exc.InvalidToken:
|
||||
self.log.info(_LI('Invalid service token'))
|
||||
request.service_token_valid = False
|
||||
else:
|
||||
request.service_token_valid = True
|
||||
|
||||
p = _user_plugin.UserAuthPlugin(user_auth_ref, serv_auth_ref)
|
||||
request.environ['keystone.token_auth'] = p
|
||||
|
||||
def _validate_token(self, auth_ref):
|
||||
"""Perform the validation steps on the token.
|
||||
@ -649,57 +681,39 @@ class AuthProtocol(_BaseAuthProtocol):
|
||||
depending on configuration.
|
||||
"""
|
||||
self._token_cache.initialize(request.environ)
|
||||
request.remove_auth_headers()
|
||||
|
||||
user_auth_ref = None
|
||||
serv_auth_ref = None
|
||||
resp = super(AuthProtocol, self).process_request(request)
|
||||
if resp:
|
||||
return resp
|
||||
|
||||
self.log.debug('Authenticating user token')
|
||||
try:
|
||||
user_token = self._get_user_token_from_request(request)
|
||||
data, user_auth_ref = self._do_fetch_token(user_token)
|
||||
self._validate_token(user_auth_ref)
|
||||
self._confirm_token_bind(user_auth_ref, request)
|
||||
except exc.InvalidToken:
|
||||
if not request.user_token:
|
||||
# if no user token is present then that's an invalid request
|
||||
request.user_token_valid = False
|
||||
|
||||
# NOTE(jamielennox): The service status is allowed to be missing if a
|
||||
# service token is not passed. If the service status is missing that's
|
||||
# a valid request. We should find a better way to expose this from the
|
||||
# request object.
|
||||
user_status = request.user_token and request.user_token_valid
|
||||
service_status = request.headers.get('X-Service-Identity-Status',
|
||||
'Confirmed')
|
||||
|
||||
if not (user_status and service_status == 'Confirmed'):
|
||||
if self._delay_auth_decision:
|
||||
self.log.info(
|
||||
_LI('Invalid user token - deferring reject '
|
||||
'downstream'))
|
||||
request.user_token_valid = False
|
||||
self.log.info(_LI('Deferring reject downstream'))
|
||||
else:
|
||||
self.log.info(
|
||||
_LI('Invalid user token - rejecting request'))
|
||||
self.log.info(_LI('Rejecting request'))
|
||||
self._reject_request()
|
||||
else:
|
||||
request.environ['keystone.token_info'] = data
|
||||
request.set_user_headers(user_auth_ref,
|
||||
|
||||
if request.user_token_valid:
|
||||
request.set_user_headers(request.token_auth._user_auth_ref,
|
||||
self._include_service_catalog)
|
||||
|
||||
if request.service_token is not None:
|
||||
self.log.debug('Authenticating service token')
|
||||
try:
|
||||
_ignore, serv_auth_ref = self._do_fetch_token(
|
||||
request.service_token)
|
||||
self._validate_token(serv_auth_ref)
|
||||
self._confirm_token_bind(serv_auth_ref, request)
|
||||
except exc.InvalidToken:
|
||||
if self._delay_auth_decision:
|
||||
self.log.info(
|
||||
_LI('Invalid service token - deferring reject '
|
||||
'downstream'))
|
||||
request.service_token_valid = False
|
||||
else:
|
||||
self.log.info(
|
||||
_LI('Invalid service token - rejecting request'))
|
||||
self._reject_request()
|
||||
else:
|
||||
request.set_service_headers(serv_auth_ref)
|
||||
|
||||
request.token_auth = _user_plugin.UserAuthPlugin(user_auth_ref,
|
||||
serv_auth_ref)
|
||||
if request.service_token and request.service_token_valid:
|
||||
request.set_service_headers(request.token_auth._serv_auth_ref)
|
||||
|
||||
if self.log.isEnabledFor(logging.DEBUG):
|
||||
self.log.debug('Received request from %s' %
|
||||
self.log.debug('Received request from %s',
|
||||
request.token_auth._log_format)
|
||||
|
||||
def process_response(self, response):
|
||||
@ -713,23 +727,6 @@ class AuthProtocol(_BaseAuthProtocol):
|
||||
|
||||
return response
|
||||
|
||||
def _get_user_token_from_request(self, request):
|
||||
"""Get token id from request.
|
||||
|
||||
:param env: wsgi request environment
|
||||
:returns: token id
|
||||
:raises exc.InvalidToken: if no token is provided in request
|
||||
|
||||
"""
|
||||
token = request.user_token
|
||||
|
||||
if token:
|
||||
return token
|
||||
else:
|
||||
if not self._delay_auth_decision:
|
||||
self.log.debug('Headers: %s', dict(request.headers))
|
||||
raise exc.InvalidToken(_('Unable to find token in headers'))
|
||||
|
||||
@property
|
||||
def _reject_auth_headers(self):
|
||||
header_val = 'Keystone uri=\'%s\'' % self._auth_uri
|
||||
|
@ -126,8 +126,13 @@ class UserAuthPlugin(base_identity.BaseIdentityPlugin):
|
||||
|
||||
def __init__(self, user_auth_ref, serv_auth_ref):
|
||||
super(UserAuthPlugin, self).__init__(reauthenticate=False)
|
||||
|
||||
# NOTE(jamielennox): _user_auth_ref and _serv_auth_ref are private
|
||||
# because this object ends up in the environ that is passed to the
|
||||
# service, however they are used within auth_token middleware.
|
||||
self._user_auth_ref = user_auth_ref
|
||||
self._serv_auth_ref = serv_auth_ref
|
||||
|
||||
self._user_data = None
|
||||
self._serv_data = None
|
||||
|
||||
|
@ -10,7 +10,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from keystoneclient import fixture
|
||||
import mock
|
||||
import six
|
||||
import testtools
|
||||
import webob
|
||||
|
||||
@ -25,6 +30,19 @@ class FakeApp(object):
|
||||
return webob.Response()
|
||||
|
||||
|
||||
class FetchingMiddleware(auth_token._BaseAuthProtocol):
|
||||
|
||||
def __init__(self, app, token_dict={}, **kwargs):
|
||||
super(FetchingMiddleware, self).__init__(app, **kwargs)
|
||||
self.token_dict = token_dict
|
||||
|
||||
def _fetch_token(self, token):
|
||||
try:
|
||||
return self.token_dict[token]
|
||||
except KeyError:
|
||||
raise auth_token.InvalidToken()
|
||||
|
||||
|
||||
class BaseAuthProtocolTests(testtools.TestCase):
|
||||
|
||||
@mock.patch.multiple(auth_token._BaseAuthProtocol,
|
||||
@ -47,3 +65,138 @@ class BaseAuthProtocolTests(testtools.TestCase):
|
||||
|
||||
self.assertEqual(1, process_response.call_count)
|
||||
self.assertIsInstance(process_response.call_args[0][0], webob.Response)
|
||||
|
||||
@classmethod
|
||||
def call(cls, middleware, method='GET', path='/', headers=None):
|
||||
req = webob.Request.blank(path)
|
||||
req.method = method
|
||||
|
||||
for k, v in six.iteritems(headers or {}):
|
||||
req.headers[k] = v
|
||||
|
||||
resp = req.get_response(middleware)
|
||||
resp.request = req
|
||||
return resp
|
||||
|
||||
def test_good_v3_user_token(self):
|
||||
t = fixture.V3Token()
|
||||
t.set_project_scope()
|
||||
role = t.add_role()
|
||||
|
||||
token_id = uuid.uuid4().hex
|
||||
token_dict = {token_id: t}
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual(token_id, req.headers['X-Auth-Token'])
|
||||
|
||||
self.assertEqual('Confirmed', req.headers['X-Identity-Status'])
|
||||
self.assertNotIn('X-Service-Token', req.headers)
|
||||
|
||||
p = req.environ['keystone.token_auth']
|
||||
|
||||
self.assertTrue(p.has_user_token)
|
||||
self.assertFalse(p.has_service_token)
|
||||
|
||||
self.assertEqual(t.project_id, p.user.project_id)
|
||||
self.assertEqual(t.project_domain_id, p.user.project_domain_id)
|
||||
self.assertEqual(t.user_id, p.user.user_id)
|
||||
self.assertEqual(t.user_domain_id, p.user.user_domain_id)
|
||||
self.assertIn(role['name'], p.user.role_names)
|
||||
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb, token_dict)
|
||||
self.call(m, headers={'X-Auth-Token': token_id})
|
||||
|
||||
def test_invalid_user_token(self):
|
||||
token_id = uuid.uuid4().hex
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual('Invalid', req.headers['X-Identity-Status'])
|
||||
self.assertEqual(token_id, req.headers['X-Auth-Token'])
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb)
|
||||
self.call(m, headers={'X-Auth-Token': token_id})
|
||||
|
||||
def test_expired_user_token(self):
|
||||
t = fixture.V3Token()
|
||||
t.set_project_scope()
|
||||
t.expires = datetime.datetime.utcnow() - datetime.timedelta(minutes=10)
|
||||
|
||||
token_id = uuid.uuid4().hex
|
||||
token_dict = {token_id: t}
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual('Invalid', req.headers['X-Identity-Status'])
|
||||
self.assertEqual(token_id, req.headers['X-Auth-Token'])
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb, token_dict=token_dict)
|
||||
self.call(m, headers={'X-Auth-Token': token_id})
|
||||
|
||||
def test_good_v3_service_token(self):
|
||||
t = fixture.V3Token()
|
||||
t.set_project_scope()
|
||||
role = t.add_role()
|
||||
|
||||
token_id = uuid.uuid4().hex
|
||||
token_dict = {token_id: t}
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual(token_id, req.headers['X-Service-Token'])
|
||||
|
||||
self.assertEqual('Confirmed',
|
||||
req.headers['X-Service-Identity-Status'])
|
||||
self.assertNotIn('X-Auth-Token', req.headers)
|
||||
|
||||
p = req.environ['keystone.token_auth']
|
||||
|
||||
self.assertFalse(p.has_user_token)
|
||||
self.assertTrue(p.has_service_token)
|
||||
|
||||
self.assertEqual(t.project_id, p.service.project_id)
|
||||
self.assertEqual(t.project_domain_id, p.service.project_domain_id)
|
||||
self.assertEqual(t.user_id, p.service.user_id)
|
||||
self.assertEqual(t.user_domain_id, p.service.user_domain_id)
|
||||
self.assertIn(role['name'], p.service.role_names)
|
||||
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb, token_dict)
|
||||
self.call(m, headers={'X-Service-Token': token_id})
|
||||
|
||||
def test_invalid_service_token(self):
|
||||
token_id = uuid.uuid4().hex
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual('Invalid',
|
||||
req.headers['X-Service-Identity-Status'])
|
||||
self.assertEqual(token_id, req.headers['X-Service-Token'])
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb)
|
||||
self.call(m, headers={'X-Service-Token': token_id})
|
||||
|
||||
def test_expired_service_token(self):
|
||||
t = fixture.V3Token()
|
||||
t.set_project_scope()
|
||||
t.expires = datetime.datetime.utcnow() - datetime.timedelta(minutes=10)
|
||||
|
||||
token_id = uuid.uuid4().hex
|
||||
token_dict = {token_id: t}
|
||||
|
||||
@webob.dec.wsgify
|
||||
def _do_cb(req):
|
||||
self.assertEqual('Invalid',
|
||||
req.headers['X-Service-Identity-Status'])
|
||||
self.assertEqual(token_id, req.headers['X-Service-Token'])
|
||||
return webob.Response()
|
||||
|
||||
m = FetchingMiddleware(_do_cb, token_dict=token_dict)
|
||||
self.call(m, headers={'X-Service-Token': token_id})
|
||||
|
Loading…
x
Reference in New Issue
Block a user