Change auth_token to use keystoneclient
The auth_token middleware should use keystoneclient for all its communication with keystone. bp auth-token-use-client Depends-On: If802e8a47e45ae00112de3739334b4b5482d0500 Change-Id: Ib6ff5b0d9b260d03fe5d120fa47035886f43439c
This commit is contained in:
parent
74d978c7ee
commit
f45d7663f3
keystonemiddleware
auth_token
tests/unit/auth_token
@ -96,7 +96,8 @@ class AuthTokenPlugin(auth.BaseAuthPlugin):
|
||||
|
||||
:param session: The session object that the auth_plugin belongs to.
|
||||
:type session: keystoneclient.session.Session
|
||||
:param tuple version: The version number required for this endpoint.
|
||||
:param version: The version number required for this endpoint.
|
||||
:type version: tuple or str
|
||||
:param str interface: what visibility the endpoint should have.
|
||||
|
||||
:returns: The base URL that will be used to talk to the required
|
||||
@ -123,9 +124,13 @@ class AuthTokenPlugin(auth.BaseAuthPlugin):
|
||||
|
||||
# NOTE(jamielennox): for backwards compatibility here we don't
|
||||
# actually use the URL from discovery we hack it up instead. :(
|
||||
if version[0] == 2:
|
||||
# NOTE(blk-u): Normalizing the version is a workaround for bug 1450272.
|
||||
# This can be removed once that's fixed. Also fix the docstring for the
|
||||
# version parameter to be just "tuple".
|
||||
version = discover.normalize_version_number(version)
|
||||
if discover.version_match((2, 0), version):
|
||||
return '%s/v2.0' % self._identity_uri
|
||||
elif version[0] == 3:
|
||||
elif discover.version_match((3, 0), version):
|
||||
return '%s/v3' % self._identity_uri
|
||||
|
||||
# NOTE(jamielennox): This plugin will only get called from auth_token
|
||||
|
@ -13,12 +13,12 @@
|
||||
from keystoneclient import auth
|
||||
from keystoneclient import discover
|
||||
from keystoneclient import exceptions
|
||||
from oslo_serialization import jsonutils
|
||||
from keystoneclient.v2_0 import client as v2_client
|
||||
from keystoneclient.v3 import client as v3_client
|
||||
from six.moves import urllib
|
||||
|
||||
from keystonemiddleware.auth_token import _auth
|
||||
from keystonemiddleware.auth_token import _exceptions as exc
|
||||
from keystonemiddleware.auth_token import _utils
|
||||
from keystonemiddleware.i18n import _, _LE, _LI, _LW
|
||||
|
||||
|
||||
@ -26,9 +26,7 @@ class _RequestStrategy(object):
|
||||
|
||||
AUTH_VERSION = None
|
||||
|
||||
def __init__(self, json_request, adap, include_service_catalog=None):
|
||||
self._json_request = json_request
|
||||
self._adapter = adap
|
||||
def __init__(self, adap, include_service_catalog=None):
|
||||
self._include_service_catalog = include_service_catalog
|
||||
|
||||
def verify_token(self, user_token):
|
||||
@ -42,36 +40,42 @@ class _V2RequestStrategy(_RequestStrategy):
|
||||
|
||||
AUTH_VERSION = (2, 0)
|
||||
|
||||
def __init__(self, adap, **kwargs):
|
||||
super(_V2RequestStrategy, self).__init__(adap, **kwargs)
|
||||
self._client = v2_client.Client(session=adap)
|
||||
|
||||
def verify_token(self, user_token):
|
||||
return self._json_request('GET',
|
||||
'/tokens/%s' % user_token,
|
||||
authenticated=True)
|
||||
token = self._client.tokens.validate_access_info(user_token)
|
||||
data = {'access': token}
|
||||
return data
|
||||
|
||||
def fetch_cert_file(self, cert_type):
|
||||
return self._adapter.get('/certificates/%s' % cert_type,
|
||||
authenticated=False)
|
||||
if cert_type == 'ca':
|
||||
return self._client.certificates.get_ca_certificate()
|
||||
elif cert_type == 'signing':
|
||||
return self._client.certificates.get_signing_certificate()
|
||||
|
||||
|
||||
class _V3RequestStrategy(_RequestStrategy):
|
||||
|
||||
AUTH_VERSION = (3, 0)
|
||||
|
||||
def verify_token(self, user_token):
|
||||
path = '/auth/tokens'
|
||||
if not self._include_service_catalog:
|
||||
path += '?nocatalog'
|
||||
def __init__(self, adap, **kwargs):
|
||||
super(_V3RequestStrategy, self).__init__(adap, **kwargs)
|
||||
self._client = v3_client.Client(session=adap)
|
||||
|
||||
return self._json_request('GET',
|
||||
path,
|
||||
authenticated=True,
|
||||
headers={'X-Subject-Token': user_token})
|
||||
def verify_token(self, user_token):
|
||||
token = self._client.tokens.validate(
|
||||
user_token,
|
||||
include_catalog=self._include_service_catalog)
|
||||
data = {'token': token}
|
||||
return data
|
||||
|
||||
def fetch_cert_file(self, cert_type):
|
||||
if cert_type == 'signing':
|
||||
cert_type = 'certificates'
|
||||
|
||||
return self._adapter.get('/OS-SIMPLE-CERT/%s' % cert_type,
|
||||
authenticated=False)
|
||||
if cert_type == 'ca':
|
||||
return self._client.simple_cert.get_ca_certificates()
|
||||
elif cert_type == 'signing':
|
||||
return self._client.simple_cert.get_certificates()
|
||||
|
||||
|
||||
_REQUEST_STRATEGIES = [_V3RequestStrategy, _V2RequestStrategy]
|
||||
@ -97,6 +101,8 @@ class IdentityServer(object):
|
||||
# Built on-demand with self._request_strategy.
|
||||
self._request_strategy_obj = None
|
||||
|
||||
self._v2_client = v2_client.Client(session=self._adapter)
|
||||
|
||||
@property
|
||||
def auth_uri(self):
|
||||
auth_uri = self._adapter.get_endpoint(interface=auth.AUTH_INTERFACE)
|
||||
@ -120,7 +126,6 @@ class IdentityServer(object):
|
||||
self._adapter.version = strategy_class.AUTH_VERSION
|
||||
|
||||
self._request_strategy_obj = strategy_class(
|
||||
self._json_request,
|
||||
self._adapter,
|
||||
include_service_catalog=self._include_service_catalog)
|
||||
|
||||
@ -163,10 +168,8 @@ class IdentityServer(object):
|
||||
:raises exc.ServiceError: if unable to authenticate token
|
||||
|
||||
"""
|
||||
user_token = _utils.safe_quote(user_token)
|
||||
|
||||
try:
|
||||
response, data = self._request_strategy.verify_token(user_token)
|
||||
data = self._request_strategy.verify_token(user_token)
|
||||
except exceptions.NotFound as e:
|
||||
self._LOG.warn(_LW('Authorization failed for token'))
|
||||
self._LOG.warn(_LW('Identity response: %s'), e.response.text)
|
||||
@ -182,23 +185,14 @@ class IdentityServer(object):
|
||||
e.http_status)
|
||||
self._LOG.warn(_LW('Identity response: %s'), e.response.text)
|
||||
else:
|
||||
if response.status_code == 200:
|
||||
return data
|
||||
|
||||
raise exc.InvalidToken()
|
||||
return data
|
||||
|
||||
def fetch_revocation_list(self):
|
||||
try:
|
||||
response, data = self._json_request(
|
||||
'GET', '/tokens/revoked',
|
||||
authenticated=True,
|
||||
endpoint_filter={'version': (2, 0)})
|
||||
data = self._v2_client.tokens.get_revoked()
|
||||
except exceptions.HTTPError as e:
|
||||
msg = _('Failed to fetch token revocation list: %d')
|
||||
raise exc.RevocationListError(msg % e.http_status)
|
||||
if response.status_code != 200:
|
||||
msg = _('Unable to fetch token revocation list.')
|
||||
raise exc.RevocationListError(msg)
|
||||
if 'signed' not in data:
|
||||
msg = _('Revocation list improperly formatted.')
|
||||
raise exc.RevocationListError(msg)
|
||||
@ -210,34 +204,9 @@ class IdentityServer(object):
|
||||
def fetch_ca_cert(self):
|
||||
return self._fetch_cert_file('ca')
|
||||
|
||||
def _json_request(self, method, path, **kwargs):
|
||||
"""HTTP request helper used to make json requests.
|
||||
|
||||
:param method: http method
|
||||
:param path: relative request url
|
||||
:param **kwargs: additional parameters used by session or endpoint
|
||||
:returns: http response object, response body parsed as json
|
||||
:raises ServerError: when unable to communicate with identity server.
|
||||
|
||||
"""
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers['Accept'] = 'application/json'
|
||||
|
||||
response = self._adapter.request(path, method, **kwargs)
|
||||
|
||||
try:
|
||||
data = jsonutils.loads(response.text)
|
||||
except ValueError:
|
||||
self._LOG.debug('Identity server did not return json-encoded body')
|
||||
data = {}
|
||||
|
||||
return response, data
|
||||
|
||||
def _fetch_cert_file(self, cert_type):
|
||||
try:
|
||||
response = self._request_strategy.fetch_cert_file(cert_type)
|
||||
text = self._request_strategy.fetch_cert_file(cert_type)
|
||||
except exceptions.HTTPError as e:
|
||||
raise exceptions.CertificateConfigError(e.details)
|
||||
if response.status_code != 200:
|
||||
raise exceptions.CertificateConfigError(response.text)
|
||||
return response.text
|
||||
return text
|
||||
|
@ -10,13 +10,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from six.moves import urllib
|
||||
|
||||
|
||||
def safe_quote(s):
|
||||
"""URL-encode strings that are not already URL-encoded."""
|
||||
return urllib.parse.quote(s) if s == urllib.parse.unquote(s) else s
|
||||
|
||||
|
||||
class MiniResp(object):
|
||||
|
||||
|
@ -1773,7 +1773,8 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
|
||||
text=self.examples.SIGNED_REVOCATION_LIST)
|
||||
|
||||
self.requests_mock.get('%s/v3/auth/tokens' % BASE_URI,
|
||||
text=self.token_response)
|
||||
text=self.token_response,
|
||||
headers={'X-Subject-Token': uuid.uuid4().hex})
|
||||
|
||||
self.set_middleware()
|
||||
|
||||
@ -2498,7 +2499,8 @@ class v3CompositeAuthTests(BaseAuthTokenMiddlewareTest,
|
||||
text=self.examples.SIGNED_REVOCATION_LIST)
|
||||
|
||||
self.requests_mock.get('%s/v3/auth/tokens' % BASE_URI,
|
||||
text=self.token_response)
|
||||
text=self.token_response,
|
||||
headers={'X-Subject-Token': uuid.uuid4().hex})
|
||||
|
||||
self.token_expected_env = dict(EXPECTED_V2_DEFAULT_ENV_RESPONSE)
|
||||
self.token_expected_env.update(EXPECTED_V3_DEFAULT_ENV_ADDITIONS)
|
||||
@ -2629,7 +2631,8 @@ class AuthProtocolLoadingTests(BaseAuthTokenMiddlewareTest):
|
||||
|
||||
self.requests_mock.get(self.CRUD_URL + '/v3/auth/tokens',
|
||||
request_headers=request_headers,
|
||||
json=user_token)
|
||||
json=user_token,
|
||||
headers={'X-Subject-Token': uuid.uuid4().hex})
|
||||
|
||||
req = webob.Request.blank('/')
|
||||
req.headers['X-Auth-Token'] = user_token_id
|
||||
|
@ -17,12 +17,6 @@ from keystonemiddleware.auth_token import _utils
|
||||
|
||||
class TokenEncodingTest(testtools.TestCase):
|
||||
|
||||
def test_unquoted_token(self):
|
||||
self.assertEqual('foo%20bar', _utils.safe_quote('foo bar'))
|
||||
|
||||
def test_quoted_token(self):
|
||||
self.assertEqual('foo%20bar', _utils.safe_quote('foo%20bar'))
|
||||
|
||||
def test_messages_encoded_as_bytes(self):
|
||||
"""Test that string are passed around as bytes for PY3."""
|
||||
msg = "This is an error"
|
||||
|
Loading…
x
Reference in New Issue
Block a user