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:
@@ -9,6 +9,11 @@ resume:
|
|||||||
Resume keystone services.
|
Resume keystone services.
|
||||||
If the keystone deployment is clustered using the hacluster charm, the
|
If the keystone deployment is clustered using the hacluster charm, the
|
||||||
corresponding hacluster unit on the node must be resumed as well.
|
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:
|
openstack-upgrade:
|
||||||
description: |
|
description: |
|
||||||
Perform openstack upgrades. Config option action-managed-upgrade must be
|
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 charmhelpers.core.hookenv import action_fail
|
||||||
|
|
||||||
from keystone_utils import (
|
from keystone_utils import (
|
||||||
|
rotate_admin_passwd,
|
||||||
pause_unit_helper,
|
pause_unit_helper,
|
||||||
resume_unit_helper,
|
resume_unit_helper,
|
||||||
register_configs,
|
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):
|
def pause(args):
|
||||||
"""Pause all the Keystone services.
|
"""Pause all the Keystone services.
|
||||||
|
|
||||||
@@ -57,7 +66,11 @@ def resume(args):
|
|||||||
|
|
||||||
# A dictionary of all the defined actions to callables (which take
|
# A dictionary of all the defined actions to callables (which take
|
||||||
# parsed arguments).
|
# parsed arguments).
|
||||||
ACTIONS = {"pause": pause, "resume": resume}
|
ACTIONS = {
|
||||||
|
"rotate-admin-password": rotate_admin_password,
|
||||||
|
"pause": pause,
|
||||||
|
"resume": resume,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
|
1
actions/rotate-admin-password
Symbolic link
1
actions/rotate-admin-password
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
actions.py
|
@@ -1500,6 +1500,22 @@ def set_admin_passwd(passwd, user=None):
|
|||||||
_leader_set_secret({'{}_passwd'.format(user): passwd})
|
_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():
|
def get_api_version():
|
||||||
api_version = config('preferred-api-version')
|
api_version = config('preferred-api-version')
|
||||||
cmp_release = CompareOpenStackReleases(
|
cmp_release = CompareOpenStackReleases(
|
||||||
|
@@ -25,6 +25,17 @@ with patch('charmhelpers.contrib.openstack.utils.'
|
|||||||
import actions.actions
|
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):
|
class PauseTestCase(CharmTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@@ -2131,3 +2131,57 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.get_add_role_to_admin({}),
|
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()
|
||||||
|
Reference in New Issue
Block a user