202 lines
8.0 KiB
Python
202 lines
8.0 KiB
Python
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||
|
# not use this file except in compliance with the License. You may obtain
|
||
|
# a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
# License for the specific language governing permissions and limitations
|
||
|
# under the License.
|
||
|
|
||
|
import datetime
|
||
|
from unittest import mock
|
||
|
|
||
|
from oslo_utils import timeutils
|
||
|
|
||
|
from keystoneclient import access
|
||
|
from keystoneclient import httpclient
|
||
|
from keystoneclient.tests.unit import utils
|
||
|
from keystoneclient.tests.unit.v2_0 import client_fixtures
|
||
|
from keystoneclient import utils as client_utils
|
||
|
|
||
|
try:
|
||
|
import keyring # noqa
|
||
|
import pickle # noqa
|
||
|
except ImportError:
|
||
|
keyring = None
|
||
|
|
||
|
|
||
|
PROJECT_SCOPED_TOKEN = client_fixtures.project_scoped_token()
|
||
|
|
||
|
# These mirror values from PROJECT_SCOPED_TOKEN
|
||
|
USERNAME = 'exampleuser'
|
||
|
AUTH_URL = 'http://public.com:5000/v2.0'
|
||
|
TOKEN = '04c7d5ffaeef485f9dc69c06db285bdb'
|
||
|
|
||
|
PASSWORD = 'password'
|
||
|
TENANT = 'tenant'
|
||
|
TENANT_ID = 'tenant_id'
|
||
|
|
||
|
|
||
|
class KeyringTest(utils.TestCase):
|
||
|
|
||
|
def setUp(self):
|
||
|
if keyring is None:
|
||
|
self.skipTest(
|
||
|
'optional package keyring or pickle is not installed')
|
||
|
|
||
|
class MemoryKeyring(keyring.backend.KeyringBackend):
|
||
|
"""A Simple testing keyring.
|
||
|
|
||
|
This class supports stubbing an initial password to be returned by
|
||
|
setting password, and allows easy password and key retrieval. Also
|
||
|
records if a password was retrieved.
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.key = None
|
||
|
self.password = None
|
||
|
self.fetched = False
|
||
|
self.get_password_called = False
|
||
|
self.set_password_called = False
|
||
|
|
||
|
def supported(self):
|
||
|
return 1
|
||
|
|
||
|
def get_password(self, service, username):
|
||
|
self.get_password_called = True
|
||
|
key = username + '@' + service
|
||
|
# make sure we don't get passwords crossed if one is enforced.
|
||
|
if self.key and self.key != key:
|
||
|
return None
|
||
|
if self.password:
|
||
|
self.fetched = True
|
||
|
return self.password
|
||
|
|
||
|
def set_password(self, service, username, password):
|
||
|
self.set_password_called = True
|
||
|
self.key = username + '@' + service
|
||
|
self.password = password
|
||
|
|
||
|
super(KeyringTest, self).setUp()
|
||
|
self.memory_keyring = MemoryKeyring()
|
||
|
keyring.set_keyring(self.memory_keyring)
|
||
|
|
||
|
def test_no_keyring_key(self):
|
||
|
"""Test case when no keyring set.
|
||
|
|
||
|
Ensure that if we don't have use_keyring set in the client that
|
||
|
the keyring is never accessed.
|
||
|
"""
|
||
|
# Creating a HTTPClient not using session is deprecated.
|
||
|
with self.deprecations.expect_deprecations_here():
|
||
|
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||
|
project_id=TENANT_ID, auth_url=AUTH_URL)
|
||
|
|
||
|
# stub and check that a new token is received
|
||
|
method = 'get_raw_token_from_identity_service'
|
||
|
with mock.patch.object(cl, method) as meth:
|
||
|
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
|
||
|
|
||
|
self.assertTrue(cl.authenticate())
|
||
|
|
||
|
self.assertEqual(1, meth.call_count)
|
||
|
|
||
|
# make sure that we never touched the keyring
|
||
|
self.assertFalse(self.memory_keyring.get_password_called)
|
||
|
self.assertFalse(self.memory_keyring.set_password_called)
|
||
|
|
||
|
def test_build_keyring_key(self):
|
||
|
# Creating a HTTPClient not using session is deprecated.
|
||
|
with self.deprecations.expect_deprecations_here():
|
||
|
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||
|
project_id=TENANT_ID, auth_url=AUTH_URL)
|
||
|
|
||
|
keyring_key = cl._build_keyring_key(auth_url=AUTH_URL,
|
||
|
username=USERNAME,
|
||
|
tenant_name=TENANT,
|
||
|
tenant_id=TENANT_ID,
|
||
|
token=TOKEN)
|
||
|
|
||
|
self.assertEqual(keyring_key,
|
||
|
'%s/%s/%s/%s/%s' %
|
||
|
(AUTH_URL, TENANT_ID, TENANT, TOKEN, USERNAME))
|
||
|
|
||
|
def test_set_and_get_keyring_expired(self):
|
||
|
# Creating a HTTPClient not using session is deprecated.
|
||
|
with self.deprecations.expect_deprecations_here():
|
||
|
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||
|
project_id=TENANT_ID, auth_url=AUTH_URL,
|
||
|
use_keyring=True)
|
||
|
|
||
|
# set an expired token into the keyring
|
||
|
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
|
||
|
expired = timeutils.utcnow() - datetime.timedelta(minutes=30)
|
||
|
auth_ref['token']['expires'] = client_utils.isotime(expired)
|
||
|
self.memory_keyring.password = pickle.dumps(auth_ref)
|
||
|
|
||
|
# stub and check that a new token is received, so not using expired
|
||
|
method = 'get_raw_token_from_identity_service'
|
||
|
with mock.patch.object(cl, method) as meth:
|
||
|
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
|
||
|
|
||
|
self.assertTrue(cl.authenticate())
|
||
|
|
||
|
self.assertEqual(1, meth.call_count)
|
||
|
|
||
|
# check that a value was returned from the keyring
|
||
|
self.assertTrue(self.memory_keyring.fetched)
|
||
|
|
||
|
# check that the new token has been loaded into the keyring
|
||
|
new_auth_ref = pickle.loads(self.memory_keyring.password)
|
||
|
self.assertEqual(new_auth_ref['token']['expires'],
|
||
|
PROJECT_SCOPED_TOKEN['access']['token']['expires'])
|
||
|
|
||
|
def test_get_keyring(self):
|
||
|
# Creating a HTTPClient not using session is deprecated.
|
||
|
with self.deprecations.expect_deprecations_here():
|
||
|
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||
|
project_id=TENANT_ID, auth_url=AUTH_URL,
|
||
|
use_keyring=True)
|
||
|
|
||
|
# set an token into the keyring
|
||
|
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
|
||
|
future = timeutils.utcnow() + datetime.timedelta(minutes=30)
|
||
|
auth_ref['token']['expires'] = client_utils.isotime(future)
|
||
|
self.memory_keyring.password = pickle.dumps(auth_ref)
|
||
|
|
||
|
# don't stub get_raw_token so will fail if authenticate happens
|
||
|
|
||
|
self.assertTrue(cl.authenticate())
|
||
|
self.assertTrue(self.memory_keyring.fetched)
|
||
|
|
||
|
def test_set_keyring(self):
|
||
|
# Creating a HTTPClient not using session is deprecated.
|
||
|
with self.deprecations.expect_deprecations_here():
|
||
|
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||
|
project_id=TENANT_ID, auth_url=AUTH_URL,
|
||
|
use_keyring=True)
|
||
|
|
||
|
# stub and check that a new token is received
|
||
|
method = 'get_raw_token_from_identity_service'
|
||
|
with mock.patch.object(cl, method) as meth:
|
||
|
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
|
||
|
|
||
|
self.assertTrue(cl.authenticate())
|
||
|
|
||
|
self.assertEqual(1, meth.call_count)
|
||
|
|
||
|
# we checked the keyring, but we didn't find anything
|
||
|
self.assertTrue(self.memory_keyring.get_password_called)
|
||
|
self.assertFalse(self.memory_keyring.fetched)
|
||
|
|
||
|
# check that the new token has been loaded into the keyring
|
||
|
self.assertTrue(self.memory_keyring.set_password_called)
|
||
|
new_auth_ref = pickle.loads(self.memory_keyring.password)
|
||
|
self.assertEqual(new_auth_ref.auth_token, TOKEN)
|
||
|
self.assertEqual(new_auth_ref['token'],
|
||
|
PROJECT_SCOPED_TOKEN['access']['token'])
|
||
|
self.assertEqual(new_auth_ref.username, USERNAME)
|