summaryrefslogtreecommitdiff
path: root/keystoneclient/tests/unit/test_keyring.py
blob: 7d30d980c6a20a9d47af8ca3ab64ec06aa46ebb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#    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

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)