Add rotate-admin-password action
This action allows the user to easily rotate the admin user's password by replacing it with a randomly generated one. Change-Id: I6ce69be15b11b00f804d3143d835ec3ce6515865 Related-Bug: #1927280 Func-Test-PR: https://github.com/openstack-charmers/zaza-openstack-tests/pull/720
This commit is contained in:
parent
4949830cea
commit
ae178d7471
|
@ -9,6 +9,11 @@ resume:
|
|||
Resume keystone services.
|
||||
If the keystone deployment is clustered using the hacluster charm, the
|
||||
corresponding hacluster unit on the node must be resumed as well.
|
||||
rotate-admin-password:
|
||||
description: |
|
||||
Rotate the admin user's password.
|
||||
The current password is replaced with a randomly generated password. The
|
||||
new password is stored in the leader's admin_passwd bucket.
|
||||
openstack-upgrade:
|
||||
description: |
|
||||
Perform openstack upgrades. Config option action-managed-upgrade must be
|
||||
|
|
|
@ -33,12 +33,21 @@ _add_path(_root)
|
|||
from charmhelpers.core.hookenv import action_fail
|
||||
|
||||
from keystone_utils import (
|
||||
rotate_admin_passwd,
|
||||
pause_unit_helper,
|
||||
resume_unit_helper,
|
||||
register_configs,
|
||||
)
|
||||
|
||||
|
||||
def rotate_admin_password(args):
|
||||
"""Rotate the admin user's password.
|
||||
|
||||
@raises Exception if keystone client cannot update the password
|
||||
"""
|
||||
rotate_admin_passwd()
|
||||
|
||||
|
||||
def pause(args):
|
||||
"""Pause all the Keystone services.
|
||||
|
||||
|
@ -57,7 +66,11 @@ def resume(args):
|
|||
|
||||
# A dictionary of all the defined actions to callables (which take
|
||||
# parsed arguments).
|
||||
ACTIONS = {"pause": pause, "resume": resume}
|
||||
ACTIONS = {
|
||||
"rotate-admin-password": rotate_admin_password,
|
||||
"pause": pause,
|
||||
"resume": resume,
|
||||
}
|
||||
|
||||
|
||||
def main(args):
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
actions.py
|
|
@ -1500,6 +1500,22 @@ def set_admin_passwd(passwd, user=None):
|
|||
_leader_set_secret({'{}_passwd'.format(user): passwd})
|
||||
|
||||
|
||||
def rotate_admin_passwd():
|
||||
if not is_leader():
|
||||
raise RuntimeError("This unit is not the leader and therefore can't "
|
||||
"rotate the admin password.")
|
||||
admin_passwd = config('admin-password')
|
||||
if admin_passwd and admin_passwd.strip().lower() != 'none':
|
||||
raise RuntimeError(
|
||||
"The 'admin-password' config is present, so the action will be "
|
||||
"aborted. To allow randomly generated passwords, unset the "
|
||||
"config value.")
|
||||
user = config('admin-user')
|
||||
new_passwd = pwgen(16)
|
||||
update_user_password(user, new_passwd, ADMIN_DOMAIN)
|
||||
leader_set({'admin_passwd': new_passwd})
|
||||
|
||||
|
||||
def get_api_version():
|
||||
api_version = config('preferred-api-version')
|
||||
cmp_release = CompareOpenStackReleases(
|
||||
|
|
|
@ -25,6 +25,17 @@ with patch('charmhelpers.contrib.openstack.utils.'
|
|||
import actions.actions
|
||||
|
||||
|
||||
class ChangeAdminPasswordTestCase(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ChangeAdminPasswordTestCase, self).setUp(
|
||||
actions.actions, ["rotate_admin_passwd"])
|
||||
|
||||
def test_rotate_admin_password(self):
|
||||
actions.actions.rotate_admin_password([])
|
||||
self.rotate_admin_passwd.assert_called_once()
|
||||
|
||||
|
||||
class PauseTestCase(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -2131,3 +2131,57 @@ class TestKeystoneUtils(CharmTestCase):
|
|||
self.assertEqual(
|
||||
utils.get_add_role_to_admin({}),
|
||||
[])
|
||||
|
||||
@patch.object(utils, 'update_user_password')
|
||||
@patch.object(utils, 'leader_set')
|
||||
@patch.object(utils, 'pwgen')
|
||||
@patch.object(utils, 'is_leader')
|
||||
def test_rotate_admin_password_without_config(
|
||||
self, is_leader, pwgen, leader_set, update_user_password):
|
||||
user = 'test-user'
|
||||
password = 'password'
|
||||
is_leader.return_value = True
|
||||
pwgen.return_value = password
|
||||
self.test_config.set('admin-user', user)
|
||||
self.test_config.set('admin-password', '')
|
||||
|
||||
utils.rotate_admin_passwd()
|
||||
|
||||
pwgen.assert_called_once()
|
||||
update_user_password.assert_called_once_with(
|
||||
user, password, utils.ADMIN_DOMAIN)
|
||||
leader_set.assert_called_once_with({'admin_passwd': password})
|
||||
|
||||
@patch.object(utils, 'update_user_password')
|
||||
@patch.object(utils, 'leader_set')
|
||||
@patch.object(utils, 'pwgen')
|
||||
@patch.object(utils, 'is_leader')
|
||||
def test_rotate_admin_password_with_config(
|
||||
self, is_leader, pwgen, leader_set, update_user_password):
|
||||
user = 'test-user'
|
||||
password = 'password'
|
||||
is_leader.return_value = True
|
||||
self.test_config.set('admin-user', user)
|
||||
self.test_config.set('admin-password', password)
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
utils.rotate_admin_passwd()
|
||||
|
||||
pwgen.assert_not_called()
|
||||
update_user_password.assert_not_called()
|
||||
leader_set.assert_not_called()
|
||||
|
||||
@patch.object(utils, 'update_user_password')
|
||||
@patch.object(utils, 'leader_set')
|
||||
@patch.object(utils, 'pwgen')
|
||||
@patch.object(utils, 'is_leader')
|
||||
def test_rotate_admin_password_outside_leader(
|
||||
self, is_leader, pwgen, leader_set, update_user_password):
|
||||
is_leader.return_value = False
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
utils.rotate_admin_passwd()
|
||||
|
||||
pwgen.assert_not_called()
|
||||
update_user_password.assert_not_called()
|
||||
leader_set.assert_not_called()
|
||||
|
|
Loading…
Reference in New Issue