diff --git a/src/redfish/rest/v1.py b/src/redfish/rest/v1.py index 4969926..d140aec 100644 --- a/src/redfish/rest/v1.py +++ b/src/redfish/rest/v1.py @@ -1012,6 +1012,17 @@ def login(self, username=None, password=None, auth=AuthMethod.SESSION, headers=N headers = headers if headers else {} if auth == AuthMethod.BASIC: + warnings.warn( + "HTTP Basic authentication is being used. " + "Note: The IETF has highlighted security concerns with HTTP Basic authentication. " + "While HTTPS is required for the usage of HTTP Basic authentication, there are other " + "concerns implementers need to be aware of that RFC7617 documents. " + "This functionality can be restricted or disabled with the HTTPBasicAuth property " + "in the AccountService resource. " + "For improved performance and security, a client should use the session management " + "interface to create a Redfish login session.", + UserWarning + ) auth_key = base64.b64encode(('%s:%s' % (self.__username, self.__password)).encode('utf-8')).decode('utf-8') self.__authorization_key = 'Basic %s' % auth_key diff --git a/tests/rest/test_v1.py b/tests/rest/test_v1.py index c953c04..2f1ebf3 100644 --- a/tests/rest/test_v1.py +++ b/tests/rest/test_v1.py @@ -74,6 +74,35 @@ def test_redfish_client_root_object_initialized_after_login( self.assertEqual(client.root, json.loads(dummy_root_data)) + @mock.patch("requests.Session.request") + def test_basic_auth_emits_warning(self, mocked_request: mock.Mock) -> None: + """Test that using basic auth emits a security warning.""" + import warnings + from redfish.rest.v1 import AuthMethod + + dummy_root_data = '{"Links": {"Sessions": {"@data.id": "/redfish/v1/SessionService/Sessions"}}}' + root_resp = mock.Mock(content=dummy_root_data, status_code=200) + auth_resp = mock.Mock(content="", status_code=200) + mocked_request.side_effect = [ + root_resp, + auth_resp, + ] + client = redfish_client(base_url=self.base_url, check_connectivity=False) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + client.login(username=self.username, password=self.password, auth=AuthMethod.BASIC) + + # Check that a warning was emitted + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[0].category, UserWarning)) + # Check that the warning contains the expected keywords + warning_message = str(w[0].message) + self.assertIn("HTTP Basic authentication", warning_message) + self.assertIn("security concerns", warning_message) + self.assertIn("RFC7617", warning_message) + self.assertIn("session management", warning_message) + if __name__ == "__main__": unittest.main()