Allow unauthenticated discovery

The default state for session requests is that if there is an auth
plugin available then it should include a token in any requests. This is
a problem for cases where it is the authentication plugin itself trying
to do discovery (like in the case of version independent plugins)
because you end up in an infinite loop.

Allow controlling the authenticated parameter on discovery requests.

Closes-Bug: #1359457
Change-Id: Ib5ab0a3a30fe79139b7b5dcaae698438281b6d36
This commit is contained in:
Jamie Lennox
2014-07-17 16:07:04 +10:00
parent 4cc1763558
commit 9cc2f7c73e
4 changed files with 55 additions and 9 deletions

View File

@@ -24,16 +24,18 @@ raw data specified in version discovery responses.
import logging
from keystoneclient import exceptions
from keystoneclient import utils
_LOGGER = logging.getLogger(__name__)
def get_version_data(session, url):
@utils.positional()
def get_version_data(session, url, authenticated=None):
"""Retrieve raw version data from a url."""
headers = {'Accept': 'application/json'}
resp = session.get(url, headers=headers)
resp = session.get(url, headers=headers, authenticated=authenticated)
try:
body_resp = resp.json()
@@ -131,8 +133,10 @@ class Discover(object):
DEPRECATED_STATUSES = ('deprecated',)
EXPERIMENTAL_STATUSES = ('experimental',)
def __init__(self, session, url):
self._data = get_version_data(session, url)
@utils.positional()
def __init__(self, session, url, authenticated=None):
self._data = get_version_data(session, url,
authenticated=authenticated)
def raw_version_data(self, allow_experimental=False,
allow_deprecated=True, allow_unknown=False):

View File

@@ -19,6 +19,7 @@ import six
from keystoneclient import _discover
from keystoneclient.auth import base
from keystoneclient import exceptions
from keystoneclient import utils
LOG = logging.getLogger(__name__)
@@ -192,7 +193,7 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
return url
try:
disc = self.get_discovery(session, url)
disc = self.get_discovery(session, url, authenticated=False)
except (exceptions.DiscoveryFailure,
exceptions.HTTPError,
exceptions.ConnectionError):
@@ -206,7 +207,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
return url
def get_discovery(self, session, url):
@utils.positional()
def get_discovery(self, session, url, authenticated=None):
"""Return the discovery object for a URL.
Check the session and the plugin cache to see if we have already
@@ -218,6 +220,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
:param Session session: A session object to discover with.
:param str url: The url to lookup.
:param bool authenticated: Include a token in the discovery call.
(optional) Defaults to None (use a token
if a plugin is installed).
:raises: DiscoveryFailure if for some reason the lookup fails.
:raises: HttpError An error from an invalid HTTP response.
@@ -241,7 +246,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
if disc:
break
else:
disc = _discover.Discover(session, url)
disc = _discover.Discover(session, url,
authenticated=authenticated)
self._endpoint_cache[url] = disc
session_endpoint_cache[url] = disc

View File

@@ -17,6 +17,7 @@ import six
from keystoneclient import _discover
from keystoneclient import exceptions
from keystoneclient import session as client_session
from keystoneclient import utils
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
@@ -44,7 +45,8 @@ class Discover(_discover.Discover):
operates upon the data that was retrieved.
"""
def __init__(self, session=None, **kwargs):
@utils.positional(2)
def __init__(self, session=None, authenticated=None, **kwargs):
"""Construct a new discovery object.
The connection parameters associated with this method are the same
@@ -98,6 +100,10 @@ class Discover(_discover.Discover):
service. default: False (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param bool authenticated: Should a token be used to perform the
initial discovery operations.
default: None (attach a token if an auth
plugin is available).
"""
if not session:
@@ -121,7 +127,8 @@ class Discover(_discover.Discover):
'auth_url or endpoint')
self._client_kwargs = kwargs
super(Discover, self).__init__(session, url)
super(Discover, self).__init__(session, url,
authenticated=authenticated)
def available_versions(self, **kwargs):
"""Return a list of identity APIs available on the server and the data

View File

@@ -10,15 +10,19 @@
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import six
from testtools import matchers
from keystoneclient import _discover
from keystoneclient.auth import token_endpoint
from keystoneclient import client
from keystoneclient import discover
from keystoneclient import exceptions
from keystoneclient import fixture
from keystoneclient.openstack.common import jsonutils
from keystoneclient import session
from keystoneclient.tests import utils
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
@@ -547,6 +551,31 @@ class ClientDiscoveryTests(utils.TestCase):
self.assertRaises(exceptions.DiscoveryFailure,
disc.create_client, version=(3, 0))
def _do_discovery_call(self, token=None, **kwargs):
self.requests.register_uri('GET', BASE_URL, status_code=300,
text=V3_VERSION_LIST)
if not token:
token = uuid.uuid4().hex
url = 'http://testurl'
a = token_endpoint.Token(url, token)
s = session.Session(auth=a)
# will default to true as there is a plugin on the session
discover.Discover(s, auth_url=BASE_URL, **kwargs)
self.assertEqual(BASE_URL, self.requests.last_request.url)
def test_setting_authenticated_true(self):
token = uuid.uuid4().hex
self._do_discovery_call(token)
self.assertRequestHeaderEqual('X-Auth-Token', token)
def test_setting_authenticated_false(self):
self._do_discovery_call(authenticated=False)
self.assertNotIn('X-Auth-Token', self.requests.last_request.headers)
class DiscoverQueryTests(utils.TestCase):