heat_keystoneclient add create_stack_domain_user function
Add function which creates users in the heat domain user domain, in the project specified (which is expected to be in the correct domain, ie created via create_stack_domain_project) This is similar to create_stack_user, which is expected to be removed when all resources have been migrated to domain users. Change-Id: Ie0a6ce039058a574ff0ddc3ed69e0e9cc9ad4db4 blueprint: instance-users
This commit is contained in:
@@ -224,6 +224,23 @@ class KeystoneClient(object):
|
||||
except kc_exception.NotFound:
|
||||
pass
|
||||
|
||||
def _get_username(self, username):
|
||||
if(len(username) > 64):
|
||||
logger.warning(_("Truncating the username %s to the last 64 "
|
||||
"characters.") % username)
|
||||
#get the last 64 characters of the username
|
||||
return username[-64:]
|
||||
|
||||
def _get_stack_user_role(self, roles_list):
|
||||
# FIXME(shardy): The currently released v3 keystoneclient doesn't
|
||||
# support filtering the results, so we have to do it locally,
|
||||
# update when a new keystoneclient release happens containing
|
||||
# the extensible-crud-manager-operations patch
|
||||
stack_user_role = [r for r in roles_list
|
||||
if r.name == self.conf.heat_stack_user_role]
|
||||
if len(stack_user_role) == 1:
|
||||
return stack_user_role[0].id
|
||||
|
||||
def create_stack_user(self, username, password=''):
|
||||
"""
|
||||
Create a user defined as part of a stack, either via template
|
||||
@@ -231,30 +248,18 @@ class KeystoneClient(object):
|
||||
the heat_stack_user_role as defined in the config
|
||||
Returns the keystone ID of the resulting user
|
||||
"""
|
||||
if(len(username) > 64):
|
||||
logger.warning(_("Truncating the username %s to the last 64 "
|
||||
"characters.") % username)
|
||||
#get the last 64 characters of the username
|
||||
username = username[-64:]
|
||||
|
||||
# We add the new user to a special keystone role
|
||||
# This role is designed to allow easier differentiation of the
|
||||
# heat-generated "stack users" which will generally have credentials
|
||||
# deployed on an instance (hence are implicitly untrusted)
|
||||
# FIXME(shardy): The v3 keystoneclient doesn't currently support
|
||||
# filtering the results, so we have to do it locally, update when
|
||||
# that is fixed in keystoneclient
|
||||
# FIXME(shardy): There's duplicated logic between here and
|
||||
# create_stack_domain user, but this function is expected to
|
||||
# be removed after the transition of all resources to domain
|
||||
# users has been completed
|
||||
roles_list = self.client_v3.roles.list()
|
||||
stack_user_role = [r for r in roles_list
|
||||
if r.name == self.conf.heat_stack_user_role]
|
||||
if len(stack_user_role) == 1:
|
||||
role_id = self._get_stack_user_role(roles_list)
|
||||
if role_id:
|
||||
# Create the user
|
||||
user = self.client_v3.users.create(
|
||||
name=username, password=password,
|
||||
name=self._get_username(username), password=password,
|
||||
default_project=self.context.tenant_id)
|
||||
|
||||
# Add user to heat_stack_user_role
|
||||
role_id = stack_user_role[0].id
|
||||
logger.debug(_("Adding user %(user)s to role %(role)s") % {
|
||||
'user': user.id, 'role': role_id})
|
||||
self.client_v3.roles.grant(role=role_id, user=user.id,
|
||||
@@ -269,6 +274,40 @@ class KeystoneClient(object):
|
||||
|
||||
return user.id
|
||||
|
||||
def create_stack_domain_user(self, username, project_id, password=None):
|
||||
"""
|
||||
Create a user defined as part of a stack, either via template
|
||||
or created internally by a resource. This user will be added to
|
||||
the heat_stack_user_role as defined in the config, and created in
|
||||
the specified project (which is expected to be in the stack_domain.
|
||||
Returns the keystone ID of the resulting user
|
||||
"""
|
||||
|
||||
# We add the new user to a special keystone role
|
||||
# This role is designed to allow easier differentiation of the
|
||||
# heat-generated "stack users" which will generally have credentials
|
||||
# deployed on an instance (hence are implicitly untrusted)
|
||||
roles_list = self.admin_client.roles.list()
|
||||
role_id = self._get_stack_user_role(roles_list)
|
||||
if role_id:
|
||||
# Create user
|
||||
user = self.admin_client.users.create(
|
||||
name=self._get_username(username), password=password,
|
||||
default_project=project_id, domain=self.stack_domain_id)
|
||||
# Add to stack user role
|
||||
logger.debug(_("Adding user %(user)s to role %(role)s") % {
|
||||
'user': user.id, 'role': role_id})
|
||||
self.admin_client.roles.grant(role=role_id, user=user.id,
|
||||
project=project_id)
|
||||
else:
|
||||
logger.error(_("Failed to add user %(user)s to role %(role)s, "
|
||||
"check role exists!") % {'user': username,
|
||||
'role': self.conf.heat_stack_user_role})
|
||||
raise exception.Error(_("Can't find role %s")
|
||||
% self.conf.heat_stack_user_role)
|
||||
|
||||
return user.id
|
||||
|
||||
def delete_stack_user(self, user_id):
|
||||
self.client_v3.users.delete(user=user_id)
|
||||
|
||||
|
||||
@@ -139,18 +139,9 @@ class KeystoneClientTest(HeatTestCase):
|
||||
password='password',
|
||||
default_project=ctx.tenant_id
|
||||
).AndReturn(mock_user)
|
||||
# mock out the call to roles; will send an error log message but does
|
||||
# not raise an exception
|
||||
self.mock_config.heat_stack_user_role = 'heat_stack_user'
|
||||
mock_roles_list = []
|
||||
for r_id, r_name in (('1234', 'blah'), ('4546', 'heat_stack_user')):
|
||||
mock_role = self.m.CreateMockAnything()
|
||||
mock_role.id = r_id
|
||||
mock_role.name = r_name
|
||||
mock_roles_list.append(mock_role)
|
||||
|
||||
self.mock_ks_v3_client.roles = self.m.CreateMockAnything()
|
||||
self.mock_ks_v3_client.roles.list().AndReturn(mock_roles_list)
|
||||
self.mock_ks_v3_client.roles.list().AndReturn(self._mock_roles_list())
|
||||
self.mock_ks_v3_client.roles.grant(project=ctx.tenant_id,
|
||||
role='4546',
|
||||
user='auser123').AndReturn(None)
|
||||
@@ -170,15 +161,8 @@ class KeystoneClientTest(HeatTestCase):
|
||||
ctx = utils.dummy_context()
|
||||
ctx.trust_id = None
|
||||
|
||||
self.mock_config.heat_stack_user_role = 'heat_stack_user'
|
||||
mock_roles_list = []
|
||||
for r_id, r_name in (('1234', 'blah'), ('4546', 'notheat_stack_user')):
|
||||
mock_role = self.m.CreateMockAnything()
|
||||
mock_role.id = r_id
|
||||
mock_role.name = r_name
|
||||
mock_roles_list.append(mock_role)
|
||||
|
||||
self.mock_ks_v3_client.roles = self.m.CreateMockAnything()
|
||||
mock_roles_list = self._mock_roles_list(heat_stack_user='badrole')
|
||||
self.mock_ks_v3_client.roles.list().AndReturn(mock_roles_list)
|
||||
self.m.ReplayAll()
|
||||
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
|
||||
@@ -187,6 +171,64 @@ class KeystoneClientTest(HeatTestCase):
|
||||
'auser', password='password')
|
||||
self.assertIn('Can\'t find role heat_stack_user', err)
|
||||
|
||||
def _mock_roles_list(self, heat_stack_user='heat_stack_user'):
|
||||
self.mock_config.heat_stack_user_role = 'heat_stack_user'
|
||||
mock_roles_list = []
|
||||
for r_id, r_name in (('1234', 'blah'), ('4546', heat_stack_user)):
|
||||
mock_role = self.m.CreateMockAnything()
|
||||
mock_role.id = r_id
|
||||
mock_role.name = r_name
|
||||
mock_roles_list.append(mock_role)
|
||||
return mock_roles_list
|
||||
|
||||
def test_create_stack_domain_user(self):
|
||||
"""Test creating a stack domain user."""
|
||||
|
||||
ctx = utils.dummy_context()
|
||||
ctx.trust_id = None
|
||||
|
||||
# mock keystone client functions
|
||||
self._stub_domain(ret_id='adomain123')
|
||||
self.mock_admin_client.users = self.m.CreateMockAnything()
|
||||
mock_user = self.m.CreateMockAnything()
|
||||
mock_user.id = 'duser123'
|
||||
self.mock_admin_client.users.create(name='duser',
|
||||
password=None,
|
||||
default_project='aproject',
|
||||
domain='adomain123'
|
||||
).AndReturn(mock_user)
|
||||
self.mock_admin_client.roles = self.m.CreateMockAnything()
|
||||
self.mock_admin_client.roles.list().AndReturn(self._mock_roles_list())
|
||||
self.mock_admin_client.roles.grant(project='aproject',
|
||||
role='4546',
|
||||
user='duser123').AndReturn(None)
|
||||
self.m.ReplayAll()
|
||||
|
||||
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
|
||||
heat_ks_client.create_stack_domain_user(username='duser',
|
||||
project_id='aproject')
|
||||
|
||||
def test_create_stack_domain_user_error_norole(self):
|
||||
"""Test creating a stack domain user, no role error path."""
|
||||
|
||||
ctx = utils.dummy_context()
|
||||
ctx.trust_id = None
|
||||
|
||||
self._stub_config()
|
||||
self._stub_admin_client()
|
||||
|
||||
# mock keystone client functions
|
||||
self.mock_admin_client.roles = self.m.CreateMockAnything()
|
||||
mock_roles_list = self._mock_roles_list(heat_stack_user='badrole')
|
||||
self.mock_admin_client.roles.list().AndReturn(mock_roles_list)
|
||||
self.m.ReplayAll()
|
||||
|
||||
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
|
||||
err = self.assertRaises(exception.Error,
|
||||
heat_ks_client.create_stack_domain_user,
|
||||
username='duser', project_id='aproject')
|
||||
self.assertIn('Can\'t find role heat_stack_user', err)
|
||||
|
||||
def test_delete_stack_user(self):
|
||||
|
||||
"""Test deleting a stack user."""
|
||||
|
||||
Reference in New Issue
Block a user