Add user group assignment support in identity

Add possibility to add/remove/check user into/from group.

Change-Id: I1e012471badcf6264dced53354472abd8312f62c
This commit is contained in:
Artem Goncharov 2021-08-26 18:19:38 +02:00
parent 21795acff6
commit d7d6464c6d
7 changed files with 192 additions and 20 deletions

View File

@ -42,7 +42,8 @@ Group Operations
.. autoclass:: openstack.identity.v3._proxy.Proxy
:noindex:
:members: create_group, update_group, delete_group, get_group, find_group,
groups
groups, add_user_to_group, remove_user_from_group,
check_user_in_group
Policy Operations
^^^^^^^^^^^^^^^^^

View File

@ -295,11 +295,7 @@ class IdentityCloudMixin(_normalize.Normalizer):
"""
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
error_msg = "Error adding user {user} to group {group}".format(
user=name_or_id, group=group_name_or_id)
self._identity_client.put(
'/groups/{g}/users/{u}'.format(g=group['id'], u=user['id']),
error_message=error_msg)
self.identity.add_user_to_group(user, group)
def is_user_in_group(self, name_or_id, group_name_or_id):
"""Check to see if a user is in a group.
@ -314,14 +310,7 @@ class IdentityCloudMixin(_normalize.Normalizer):
"""
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
try:
self._identity_client.head(
'/groups/{g}/users/{u}'.format(g=group['id'], u=user['id']))
return True
except exc.OpenStackCloudURINotFound:
# NOTE(samueldmq): knowing this URI exists, let's interpret this as
# user not found in group rather than URI not found.
return False
return self.identity.check_user_in_group(user, group)
def remove_user_from_group(self, name_or_id, group_name_or_id):
"""Remove a user from a group.
@ -334,11 +323,7 @@ class IdentityCloudMixin(_normalize.Normalizer):
"""
user, group = self._get_user_and_group(name_or_id, group_name_or_id)
error_msg = "Error removing user {user} from group {group}".format(
user=name_or_id, group=group_name_or_id)
self._identity_client.delete(
'/groups/{g}/users/{u}'.format(g=group['id'], u=user['id']),
error_message=error_msg)
self.identity.remove_user_from_group(user, group)
@_utils.valid_kwargs('type', 'service_type', 'description')
def create_service(self, name, enabled=True, **kwargs):

View File

@ -362,6 +362,45 @@ class Proxy(proxy.Proxy):
"""
return self._update(_group.Group, group, **attrs)
def add_user_to_group(self, user, group):
"""Add user to group
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:return: ``None``
"""
user = self._get_resource(_user.User, user)
group = self._get_resource(_group.Group, group)
group.add_user(self, user)
def remove_user_from_group(self, user, group):
"""Remove user to group
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:return: ``None``
"""
user = self._get_resource(_user.User, user)
group = self._get_resource(_group.Group, group)
group.remove_user(self, user)
def check_user_in_group(self, user, group):
"""Check whether user belongsto group
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:return: A boolean representing current relation
"""
user = self._get_resource(_user.User, user)
group = self._get_resource(_group.Group, group)
return group.check_user(self, user)
def create_policy(self, **attrs):
"""Create a new policy from attributes

View File

@ -10,7 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack import exceptions
from openstack import resource
from openstack import utils
class Group(resource.Resource):
@ -40,3 +42,31 @@ class Group(resource.Resource):
domain_id = resource.Body('domain_id')
#: Unique group name, within the owning domain. *Type: string*
name = resource.Body('name')
def add_user(self, session, user):
"""Add user to the group"""
url = utils.urljoin(
self.base_path, self.id, 'users', user.id)
resp = session.put(url,)
exceptions.raise_from_response(resp)
def remove_user(self, session, user):
"""Remove user from the group"""
url = utils.urljoin(
self.base_path, self.id, 'users', user.id)
resp = session.delete(url,)
exceptions.raise_from_response(resp)
def check_user(self, session, user):
"""Check whether user belongs to group"""
url = utils.urljoin(
self.base_path, self.id, 'users', user.id)
resp = session.head(url,)
if resp.status_code == 404:
# If we recieve 404 - treat this as False,
# rather then returning exception
return False
exceptions.raise_from_response(resp)
if resp.status_code == 204:
return True
return False

View File

@ -10,7 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from keystoneauth1 import adapter
from openstack.identity.v3 import group
from openstack.identity.v3 import user
from openstack.tests.unit import base
@ -25,6 +30,16 @@ EXAMPLE = {
class TestGroup(base.TestCase):
def setUp(self):
super(TestGroup, self).setUp()
self.sess = mock.Mock(spec=adapter.Adapter)
self.sess.default_microversion = 1
self.sess._get_connection = mock.Mock(return_value=self.cloud)
self.good_resp = mock.Mock()
self.good_resp.body = None
self.good_resp.json = mock.Mock(return_value=self.good_resp.body)
self.good_resp.status_code = 204
def test_basic(self):
sot = group.Group()
self.assertEqual('group', sot.resource_key)
@ -52,3 +67,38 @@ class TestGroup(base.TestCase):
self.assertEqual(EXAMPLE['domain_id'], sot.domain_id)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['name'], sot.name)
def test_add_user(self):
sot = group.Group(**EXAMPLE)
resp = self.good_resp
self.sess.put = mock.Mock(return_value=resp)
sot.add_user(
self.sess, user.User(id='1'))
self.sess.put.assert_called_with(
'groups/IDENTIFIER/users/1')
def test_remove_user(self):
sot = group.Group(**EXAMPLE)
resp = self.good_resp
self.sess.delete = mock.Mock(return_value=resp)
sot.remove_user(
self.sess, user.User(id='1'))
self.sess.delete.assert_called_with(
'groups/IDENTIFIER/users/1')
def test_check_user(self):
sot = group.Group(**EXAMPLE)
resp = self.good_resp
self.sess.head = mock.Mock(return_value=resp)
self.assertTrue(
sot.check_user(
self.sess,
user.User(id='1')))
self.sess.head.assert_called_with(
'groups/IDENTIFIER/users/1')

View File

@ -35,7 +35,7 @@ class TestIdentityProxyBase(test_proxy_base.TestProxyBase):
self.proxy = _proxy.Proxy(self.session)
class TestIdentityProxy(TestIdentityProxyBase):
class TestIdentityProxyCredential(TestIdentityProxyBase):
def test_credential_create_attrs(self):
self.verify_create(self.proxy.create_credential,
@ -61,6 +61,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_credential_update(self):
self.verify_update(self.proxy.update_credential, credential.Credential)
class TestIdentityProxyDomain(TestIdentityProxyBase):
def test_domain_create_attrs(self):
self.verify_create(self.proxy.create_domain, domain.Domain)
@ -82,6 +85,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_domain_update(self):
self.verify_update(self.proxy.update_domain, domain.Domain)
class TestIdentityProxyEndpoint(TestIdentityProxyBase):
def test_endpoint_create_attrs(self):
self.verify_create(self.proxy.create_endpoint, endpoint.Endpoint)
@ -105,6 +111,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_endpoint_update(self):
self.verify_update(self.proxy.update_endpoint, endpoint.Endpoint)
class TestIdentityProxyGroup(TestIdentityProxyBase):
def test_group_create_attrs(self):
self.verify_create(self.proxy.create_group, group.Group)
@ -126,6 +135,42 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_group_update(self):
self.verify_update(self.proxy.update_group, group.Group)
def test_add_user_to_group(self):
self._verify(
"openstack.identity.v3.group.Group.add_user",
self.proxy.add_user_to_group,
method_args=['uid', 'gid'],
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
]
)
def test_remove_user_from_group(self):
self._verify(
"openstack.identity.v3.group.Group.remove_user",
self.proxy.remove_user_from_group,
method_args=['uid', 'gid'],
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
]
)
def test_check_user_in_group(self):
self._verify(
"openstack.identity.v3.group.Group.check_user",
self.proxy.check_user_in_group,
method_args=['uid', 'gid'],
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
]
)
class TestIdentityProxyPolicy(TestIdentityProxyBase):
def test_policy_create_attrs(self):
self.verify_create(self.proxy.create_policy, policy.Policy)
@ -147,6 +192,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_policy_update(self):
self.verify_update(self.proxy.update_policy, policy.Policy)
class TestIdentityProxyProject(TestIdentityProxyBase):
def test_project_create_attrs(self):
self.verify_create(self.proxy.create_project, project.Project)
@ -176,6 +224,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_project_update(self):
self.verify_update(self.proxy.update_project, project.Project)
class TestIdentityProxyService(TestIdentityProxyBase):
def test_service_create_attrs(self):
self.verify_create(self.proxy.create_service, service.Service)
@ -197,6 +248,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_service_update(self):
self.verify_update(self.proxy.update_service, service.Service)
class TestIdentityProxyUser(TestIdentityProxyBase):
def test_user_create_attrs(self):
self.verify_create(self.proxy.create_user, user.User)
@ -218,6 +272,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_user_update(self):
self.verify_update(self.proxy.update_user, user.User)
class TestIdentityProxyTrust(TestIdentityProxyBase):
def test_trust_create_attrs(self):
self.verify_create(self.proxy.create_trust, trust.Trust)
@ -236,6 +293,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_trusts(self):
self.verify_list(self.proxy.trusts, trust.Trust)
class TestIdentityProxyRegion(TestIdentityProxyBase):
def test_region_create_attrs(self):
self.verify_create(self.proxy.create_region, region.Region)
@ -257,6 +317,9 @@ class TestIdentityProxy(TestIdentityProxyBase):
def test_region_update(self):
self.verify_update(self.proxy.update_region, region.Region)
class TestIdentityProxyRole(TestIdentityProxyBase):
def test_role_create_attrs(self):
self.verify_create(self.proxy.create_role, role.Role)

View File

@ -0,0 +1,4 @@
---
features:
- |
Add support for user group assignments in identity service.