Add user group assignment API
Add methods for adding and removing users to/from groups and checking for membership. This also updates the task_manager code to look for more types that we don't want to munchify because one of the new keystone API methods being called returned a bool value. This replaces Ida3cff3acdc1406c5e6d61500766a292565191fc Change-Id: Ib34c116010312ed26b042621fcf2e7b5b774424f
This commit is contained in:
parent
0189dd248a
commit
72190ec191
@ -42,6 +42,21 @@ class UserGet(task_manager.Task):
|
||||
return client.keystone_client.users.get(**self.args)
|
||||
|
||||
|
||||
class UserAddToGroup(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.users.add_to_group(**self.args)
|
||||
|
||||
|
||||
class UserCheckInGroup(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.users.check_in_group(**self.args)
|
||||
|
||||
|
||||
class UserRemoveFromGroup(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.users.remove_from_group(**self.args)
|
||||
|
||||
|
||||
class ProjectList(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client._project_manager.list()
|
||||
|
@ -554,6 +554,86 @@ class OpenStackCloud(object):
|
||||
self.list_users.invalidate(self)
|
||||
return True
|
||||
|
||||
def _get_user_and_group(self, user_name_or_id, group_name_or_id):
|
||||
user = self.get_user(user_name_or_id)
|
||||
if not user:
|
||||
raise OpenStackCloudException(
|
||||
'User {user} not found'.format(user=user_name_or_id))
|
||||
|
||||
group = self.get_group(group_name_or_id)
|
||||
if not group:
|
||||
raise OpenStackCloudException(
|
||||
'Group {user} not found'.format(user=group_name_or_id))
|
||||
|
||||
return (user, group)
|
||||
|
||||
def add_user_to_group(self, name_or_id, group_name_or_id):
|
||||
"""Add a user to a group.
|
||||
|
||||
:param string name_or_id: User name or ID
|
||||
:param string group_name_or_id: Group name or ID
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API call
|
||||
"""
|
||||
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
|
||||
|
||||
with _utils.shade_exceptions(
|
||||
"Error adding user {user} to group {group}".format(
|
||||
user=name_or_id, group=group_name_or_id)
|
||||
):
|
||||
self.manager.submitTask(
|
||||
_tasks.UserAddToGroup(user=user['id'], group=group['id'])
|
||||
)
|
||||
|
||||
def is_user_in_group(self, name_or_id, group_name_or_id):
|
||||
"""Check to see if a user is in a group.
|
||||
|
||||
:param string name_or_id: User name or ID
|
||||
:param string group_name_or_id: Group name or ID
|
||||
|
||||
:returns: True if user is in the group, False otherwise
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API call
|
||||
"""
|
||||
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
|
||||
|
||||
try:
|
||||
return self.manager.submitTask(
|
||||
_tasks.UserCheckInGroup(user=user['id'], group=group['id'])
|
||||
)
|
||||
except keystoneauth1.exceptions.http.NotFound:
|
||||
# Because the keystone API returns either True or raises an
|
||||
# exception, which is awesome.
|
||||
return False
|
||||
except OpenStackCloudException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise OpenStackCloudException(
|
||||
"Error adding user {user} to group {group}: {err}".format(
|
||||
user=name_or_id, group=group_name_or_id, err=str(e))
|
||||
)
|
||||
|
||||
def remove_user_from_group(self, name_or_id, group_name_or_id):
|
||||
"""Remove a user from a group.
|
||||
|
||||
:param string name_or_id: User name or ID
|
||||
:param string group_name_or_id: Group name or ID
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API call
|
||||
"""
|
||||
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
|
||||
|
||||
with _utils.shade_exceptions(
|
||||
"Error removing user {user} from group {group}".format(
|
||||
user=name_or_id, group=group_name_or_id)
|
||||
):
|
||||
self.manager.submitTask(
|
||||
_tasks.UserRemoveFromGroup(user=user['id'], group=group['id'])
|
||||
)
|
||||
|
||||
@property
|
||||
def glance_client(self):
|
||||
if self._glance_client is None:
|
||||
|
@ -79,9 +79,11 @@ class Task(object):
|
||||
if self._exception:
|
||||
six.reraise(type(self._exception), self._exception,
|
||||
self._traceback)
|
||||
|
||||
if type(self._result) == list:
|
||||
return meta.obj_list_to_dict(self._result)
|
||||
elif not isinstance(self._result, types.GeneratorType):
|
||||
elif type(self._result) not in (bool, int, float, str, set,
|
||||
types.GeneratorType):
|
||||
return meta.obj_to_dict(self._result)
|
||||
else:
|
||||
return self._result
|
||||
|
@ -19,9 +19,6 @@ test_users
|
||||
Functional tests for `shade` user methods.
|
||||
"""
|
||||
|
||||
import random
|
||||
import string
|
||||
|
||||
from shade import operator_cloud
|
||||
from shade import OpenStackCloudException
|
||||
from shade.tests import base
|
||||
@ -31,8 +28,7 @@ class TestUsers(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestUsers, self).setUp()
|
||||
self.cloud = operator_cloud(cloud='devstack-admin')
|
||||
self.user_prefix = 'test_user' + ''.join(
|
||||
random.choice(string.ascii_lowercase) for _ in range(5))
|
||||
self.user_prefix = self.getUniqueString('user')
|
||||
self.addCleanup(self._cleanup_users)
|
||||
|
||||
def _cleanup_users(self):
|
||||
@ -111,3 +107,28 @@ class TestUsers(base.TestCase):
|
||||
self.assertEqual(user_name + '2', new_user['name'])
|
||||
self.assertEqual('somebody@nowhere.com', new_user['email'])
|
||||
self.assertFalse(new_user['enabled'])
|
||||
|
||||
def test_users_and_groups(self):
|
||||
if self.cloud.cloud_config.get_api_version('identity') in ('2', '2.0'):
|
||||
self.skipTest('Identity service does not support groups')
|
||||
|
||||
group_name = self.getUniqueString('group')
|
||||
self.addCleanup(self.cloud.delete_group, group_name)
|
||||
|
||||
# Create a group
|
||||
group = self.cloud.create_group(group_name, 'test group')
|
||||
self.assertIsNotNone(group)
|
||||
|
||||
# Create a user
|
||||
user_name = self.user_prefix + '_ug'
|
||||
user_email = 'nobody@nowhere.com'
|
||||
user = self._create_user(name=user_name, email=user_email)
|
||||
self.assertIsNotNone(user)
|
||||
|
||||
# Add the user to the group
|
||||
self.cloud.add_user_to_group(user_name, group_name)
|
||||
self.assertTrue(self.cloud.is_user_in_group(user_name, group_name))
|
||||
|
||||
# Remove them from the group
|
||||
self.cloud.remove_user_from_group(user_name, group_name)
|
||||
self.assertFalse(self.cloud.is_user_in_group(user_name, group_name))
|
||||
|
@ -13,10 +13,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import mock
|
||||
import types
|
||||
|
||||
import shade
|
||||
from shade import task_manager
|
||||
from shade.tests.unit import base
|
||||
|
||||
@ -35,6 +33,31 @@ class TestTaskGenerator(task_manager.Task):
|
||||
yield 1
|
||||
|
||||
|
||||
class TestTaskInt(task_manager.Task):
|
||||
def main(self, client):
|
||||
return int(1)
|
||||
|
||||
|
||||
class TestTaskFloat(task_manager.Task):
|
||||
def main(self, client):
|
||||
return float(2.0)
|
||||
|
||||
|
||||
class TestTaskStr(task_manager.Task):
|
||||
def main(self, client):
|
||||
return "test"
|
||||
|
||||
|
||||
class TestTaskBool(task_manager.Task):
|
||||
def main(self, client):
|
||||
return True
|
||||
|
||||
|
||||
class TestTaskSet(task_manager.Task):
|
||||
def main(self, client):
|
||||
return set([1, 2])
|
||||
|
||||
|
||||
class TestTaskManager(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -50,10 +73,26 @@ class TestTaskManager(base.TestCase):
|
||||
"""
|
||||
self.assertRaises(TestException, self.manager.submitTask, TestTask())
|
||||
|
||||
@mock.patch.object(shade.meta, 'obj_to_dict')
|
||||
@mock.patch.object(shade.meta, 'obj_list_to_dict')
|
||||
def test_dont_munchify_generators(self, mock_ol2d, mock_o2d):
|
||||
def test_dont_munchify_generators(self):
|
||||
ret = self.manager.submitTask(TestTaskGenerator())
|
||||
self.assertEqual(types.GeneratorType, type(ret))
|
||||
self.assertFalse(mock_o2d.called)
|
||||
self.assertFalse(mock_ol2d.called)
|
||||
self.assertIsInstance(ret, types.GeneratorType)
|
||||
|
||||
def test_dont_munchify_int(self):
|
||||
ret = self.manager.submitTask(TestTaskInt())
|
||||
self.assertIsInstance(ret, int)
|
||||
|
||||
def test_dont_munchify_float(self):
|
||||
ret = self.manager.submitTask(TestTaskFloat())
|
||||
self.assertIsInstance(ret, float)
|
||||
|
||||
def test_dont_munchify_str(self):
|
||||
ret = self.manager.submitTask(TestTaskStr())
|
||||
self.assertIsInstance(ret, str)
|
||||
|
||||
def test_dont_munchify_bool(self):
|
||||
ret = self.manager.submitTask(TestTaskBool())
|
||||
self.assertIsInstance(ret, bool)
|
||||
|
||||
def test_dont_munchify_set(self):
|
||||
ret = self.manager.submitTask(TestTaskSet())
|
||||
self.assertIsInstance(ret, set)
|
||||
|
62
shade/tests/unit/test_users.py
Normal file
62
shade/tests/unit/test_users.py
Normal file
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import mock
|
||||
import munch
|
||||
|
||||
import shade
|
||||
from shade.tests.unit import base
|
||||
|
||||
|
||||
class TestUsers(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestUsers, self).setUp()
|
||||
self.cloud = shade.operator_cloud(validate=False)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'get_user')
|
||||
@mock.patch.object(shade.OperatorCloud, 'get_group')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_add_user_to_group(self, mock_keystone, mock_group, mock_user):
|
||||
mock_user.return_value = munch.Munch(dict(id=1))
|
||||
mock_group.return_value = munch.Munch(dict(id=2))
|
||||
self.cloud.add_user_to_group("user", "group")
|
||||
mock_keystone.users.add_to_group.assert_called_once_with(
|
||||
user=1, group=2
|
||||
)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'get_user')
|
||||
@mock.patch.object(shade.OperatorCloud, 'get_group')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_is_user_in_group(self, mock_keystone, mock_group, mock_user):
|
||||
mock_user.return_value = munch.Munch(dict(id=1))
|
||||
mock_group.return_value = munch.Munch(dict(id=2))
|
||||
mock_keystone.users.check_in_group.return_value = True
|
||||
self.assertTrue(self.cloud.is_user_in_group("user", "group"))
|
||||
mock_keystone.users.check_in_group.assert_called_once_with(
|
||||
user=1, group=2
|
||||
)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'get_user')
|
||||
@mock.patch.object(shade.OperatorCloud, 'get_group')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_remove_user_from_group(self, mock_keystone, mock_group,
|
||||
mock_user):
|
||||
mock_user.return_value = munch.Munch(dict(id=1))
|
||||
mock_group.return_value = munch.Munch(dict(id=2))
|
||||
self.cloud.remove_user_from_group("user", "group")
|
||||
mock_keystone.users.remove_from_group.assert_called_once_with(
|
||||
user=1, group=2
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user