diff --git a/cloudbaseinit/conf/default.py b/cloudbaseinit/conf/default.py index 8f8be3cb..ee6a0e3d 100644 --- a/cloudbaseinit/conf/default.py +++ b/cloudbaseinit/conf/default.py @@ -291,6 +291,10 @@ class GlobalOptions(conf_base.Options): help='Ephemeral disk data loss warning path, relative to the ' 'ephemeral disk volume path. E.g.: ' 'DATALOSS_WARNING_README.txt'), + cfg.IntOpt( + 'user_password_length', default=20, + help='The length of the generated password for the user ' + 'defined by the `username` config option.'), ] self._cli_options = [ diff --git a/cloudbaseinit/osutils/windows.py b/cloudbaseinit/osutils/windows.py index d58a7905..0d3b9e9b 100644 --- a/cloudbaseinit/osutils/windows.py +++ b/cloudbaseinit/osutils/windows.py @@ -1227,6 +1227,9 @@ class WindowsUtils(base.BaseOSUtils): :volume_path_names_len.value - 1].split('\0') if n] def generate_random_password(self, length): + if length < 3: + raise exception.CloudbaseInitException( + "Password can not have less than 3 characters!") while True: pwd = super(WindowsUtils, self).generate_random_password(length) # Make sure that the Windows complexity requirements are met: diff --git a/cloudbaseinit/plugins/common/createuser.py b/cloudbaseinit/plugins/common/createuser.py index d9b01f60..5fed13b8 100644 --- a/cloudbaseinit/plugins/common/createuser.py +++ b/cloudbaseinit/plugins/common/createuser.py @@ -50,8 +50,7 @@ class BaseCreateUserPlugin(base.BasePlugin): def _get_password(osutils): # Generate a temporary random password to be replaced # by SetUserPasswordPlugin (starting from Grizzly) - maximum_length = osutils.get_maximum_password_length() - return osutils.generate_random_password(maximum_length) + return osutils.generate_random_password(CONF.user_password_length) def execute(self, service, shared_data): user_name = service.get_admin_username() or CONF.username diff --git a/cloudbaseinit/plugins/common/setuserpassword.py b/cloudbaseinit/plugins/common/setuserpassword.py index 55c9e355..a4cbabae 100644 --- a/cloudbaseinit/plugins/common/setuserpassword.py +++ b/cloudbaseinit/plugins/common/setuserpassword.py @@ -90,9 +90,8 @@ class SetUserPasswordPlugin(base.BasePlugin): password, injected = self._get_password(service, shared_data) if not password: LOG.debug('Generating a random user password') - maximum_length = osutils.get_maximum_password_length() password = osutils.generate_random_password( - maximum_length) + CONF.user_password_length) osutils.set_user_password(user_name, password) self._change_logon_behaviour(user_name, password_injected=injected) diff --git a/cloudbaseinit/tests/osutils/test_windows.py b/cloudbaseinit/tests/osutils/test_windows.py index 6e0386c9..47061876 100644 --- a/cloudbaseinit/tests/osutils/test_windows.py +++ b/cloudbaseinit/tests/osutils/test_windows.py @@ -1376,6 +1376,12 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase): mock_generate_random_password.assert_called_once_with(length) self.assertEqual('Passw0rd', response) + def test_generate_random_password_less_than_3(self): + with self.assertRaises(exception.CloudbaseInitException) as ex: + self._winutils.generate_random_password(2) + self.assertEqual(str(ex.exception), + "Password can not have less than 3 characters!") + def _test_get_logical_drives(self, buf_length, last_error=None): mock_buf = mock.MagicMock() mock_buf.__getitem__.side_effect = ['1', '\x00'] diff --git a/cloudbaseinit/tests/plugins/common/test_createuser.py b/cloudbaseinit/tests/plugins/common/test_createuser.py index 461eaded..7f365a5e 100644 --- a/cloudbaseinit/tests/plugins/common/test_createuser.py +++ b/cloudbaseinit/tests/plugins/common/test_createuser.py @@ -42,13 +42,17 @@ class CreateUserPluginTests(unittest.TestCase): self._create_user = CreateUserPlugin() def test_get_password(self): + password = "fake password" mock_osutils = mock.MagicMock() - mock_osutils.generate_random_password.return_value = 'fake password' - response = self._create_user._get_password(mock_osutils) - mock_osutils.get_maximum_password_length.assert_called_once_with() - length = mock_osutils.get_maximum_password_length() - mock_osutils.generate_random_password.assert_called_once_with(length) - self.assertEqual('fake password', response) + max_length = len(password) + mock_osutils.generate_random_password.return_value = "*" * max_length + with testutils.ConfPatcher('user_password_length', len(password)): + response = self._create_user._get_password(mock_osutils) + + mock_osutils.generate_random_password.assert_called_once_with( + max_length) + self.assertEqual("*" * max_length, response) + self.assertEqual(len(response), max_length) @testutils.ConfPatcher('groups', ['Admins']) @mock.patch('cloudbaseinit.osutils.factory.get_os_utils') diff --git a/cloudbaseinit/tests/plugins/common/test_setuserpassword.py b/cloudbaseinit/tests/plugins/common/test_setuserpassword.py index 19028887..6b8c148c 100644 --- a/cloudbaseinit/tests/plugins/common/test_setuserpassword.py +++ b/cloudbaseinit/tests/plugins/common/test_setuserpassword.py @@ -173,7 +173,8 @@ class SetUserPasswordPluginTests(unittest.TestCase): def _test_set_password(self, mock_get_password, mock_change_logon_behaviour, password, can_update_password, - is_password_changed, injected=False): + is_password_changed, max_password_length=20, + injected=False): expected_password = password expected_logging = [] user = 'fake_user' @@ -182,16 +183,20 @@ class SetUserPasswordPluginTests(unittest.TestCase): mock_service = mock.MagicMock() mock_osutils = mock.MagicMock() - mock_osutils.get_maximum_password_length.return_value = None + if not password: + expected_password = "*" * CONF.user_password_length + mock_osutils.generate_random_password.return_value = expected_password mock_service.can_update_password = can_update_password mock_service.is_password_changed.return_value = is_password_changed - with testutils.LogSnatcher('cloudbaseinit.plugins.common.' - 'setuserpassword') as snatcher: - response = self._setpassword_plugin._set_password( - mock_service, mock_osutils, user, - mock.sentinel.shared_data) + with testutils.ConfPatcher('user_password_length', + max_password_length): + with testutils.LogSnatcher('cloudbaseinit.plugins.common.' + 'setuserpassword') as snatcher: + response = self._setpassword_plugin._set_password( + mock_service, mock_osutils, user, + mock.sentinel.shared_data) if can_update_password and not is_password_changed: expected_logging.append('Updating password is not required.') @@ -199,7 +204,6 @@ class SetUserPasswordPluginTests(unittest.TestCase): if not password: expected_logging.append('Generating a random user password') - expected_password = password if not can_update_password or is_password_changed: mock_get_password.assert_called_once_with( @@ -217,7 +221,12 @@ class SetUserPasswordPluginTests(unittest.TestCase): is_password_changed=False) self._test_set_password(password=None, can_update_password=False, - is_password_changed=False) + is_password_changed=False, + max_password_length=25) + self._test_set_password(password=None, + can_update_password=False, + is_password_changed=False, + max_password_length=10) self._test_set_password(password='Password', can_update_password=True, is_password_changed=True)