diff options
Diffstat (limited to 'ironic')
-rw-r--r-- | ironic/drivers/modules/redfish/utils.py | 61 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/redfish/test_utils.py | 16 |
2 files changed, 55 insertions, 22 deletions
diff --git a/ironic/drivers/modules/redfish/utils.py b/ironic/drivers/modules/redfish/utils.py index 40cf33bce..e85e2ec6a 100644 --- a/ironic/drivers/modules/redfish/utils.py +++ b/ironic/drivers/modules/redfish/utils.py @@ -15,6 +15,7 @@ # under the License. import collections +import hashlib import os from urllib import parse as urlparse @@ -198,43 +199,59 @@ class SessionCache(object): _sessions = collections.OrderedDict() def __init__(self, driver_info): + # Hash the password in the data structure, so we can + # include it in the session key. + # NOTE(TheJulia): Multiplying the address by 4, to ensure + # we meet a minimum of 16 bytes for salt. + pw_hash = hashlib.pbkdf2_hmac( + 'sha512', + driver_info.get('password').encode('utf-8'), + str(driver_info.get('address') * 4).encode('utf-8'), 40) self._driver_info = driver_info + # Assemble the session key and append the hashed password to it, + # which forces new sessions to be established when the saved password + # is changed, just like the username, or address. self._session_key = tuple( self._driver_info.get(key) for key in ('address', 'username', 'verify_ca') - ) + ) + (pw_hash.hex(),) def __enter__(self): try: return self.__class__._sessions[self._session_key] - except KeyError: - auth_type = self._driver_info['auth_type'] + LOG.debug('A cached redfish session for Redfish endpoint ' + '%(endpoint)s was not detected, initiating a session.', + {'endpoint': self._driver_info['address']}) - auth_class = self.AUTH_CLASSES[auth_type] + auth_type = self._driver_info['auth_type'] - authenticator = auth_class( - username=self._driver_info['username'], - password=self._driver_info['password'] - ) + auth_class = self.AUTH_CLASSES[auth_type] - sushy_params = {'verify': self._driver_info['verify_ca'], - 'auth': authenticator} - if 'root_prefix' in self._driver_info: - sushy_params['root_prefix'] = self._driver_info['root_prefix'] - conn = sushy.Sushy( - self._driver_info['address'], - **sushy_params - ) + authenticator = auth_class( + username=self._driver_info['username'], + password=self._driver_info['password'] + ) + + sushy_params = {'verify': self._driver_info['verify_ca'], + 'auth': authenticator} + if 'root_prefix' in self._driver_info: + sushy_params['root_prefix'] = self._driver_info['root_prefix'] + conn = sushy.Sushy( + self._driver_info['address'], + **sushy_params + ) - if CONF.redfish.connection_cache_size: - self.__class__._sessions[self._session_key] = conn + if CONF.redfish.connection_cache_size: + self.__class__._sessions[self._session_key] = conn + # Save a secure hash of the password into memory, so if we + # observe it change, we can detect the session is no longer valid. - if (len(self.__class__._sessions) - > CONF.redfish.connection_cache_size): - self._expire_oldest_session() + if (len(self.__class__._sessions) + > CONF.redfish.connection_cache_size): + self._expire_oldest_session() - return conn + return conn def __exit__(self, exc_type, exc_val, exc_tb): # NOTE(etingof): perhaps this session token is no good diff --git a/ironic/tests/unit/drivers/modules/redfish/test_utils.py b/ironic/tests/unit/drivers/modules/redfish/test_utils.py index ca8aba9da..01b7089c7 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_utils.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_utils.py @@ -252,6 +252,7 @@ class RedfishUtilsAuthTestCase(db_base.DbTestCase): redfish_utils.get_system(self.node) redfish_utils.get_system(self.node) self.assertEqual(1, mock_sushy.call_count) + self.assertEqual(len(redfish_utils.SessionCache._sessions), 1) @mock.patch.object(sushy, 'Sushy', autospec=True) def test_ensure_new_session_address(self, mock_sushy): @@ -270,6 +271,21 @@ class RedfishUtilsAuthTestCase(db_base.DbTestCase): self.assertEqual(2, mock_sushy.call_count) @mock.patch.object(sushy, 'Sushy', autospec=True) + def test_ensure_new_session_password(self, mock_sushy): + d_info = self.node.driver_info + d_info['redfish_username'] = 'foo' + d_info['redfish_password'] = 'bar' + self.node.driver_info = d_info + self.node.save() + redfish_utils.get_system(self.node) + d_info['redfish_password'] = 'foo' + self.node.driver_info = d_info + self.node.save() + redfish_utils.SessionCache._sessions = collections.OrderedDict() + redfish_utils.get_system(self.node) + self.assertEqual(2, mock_sushy.call_count) + + @mock.patch.object(sushy, 'Sushy', autospec=True) @mock.patch('ironic.drivers.modules.redfish.utils.' 'SessionCache.AUTH_CLASSES', autospec=True) @mock.patch('ironic.drivers.modules.redfish.utils.SessionCache._sessions', |