Display users with role through group in users project details tab.

In the users tab of the project details view, the users
table displays now the users which have a role on the project
through a group. ie: if a group is member of the project, and
if a user is a member of this group, then it will be displayed
in the table even if it is not directly a member of the project.

An extra column is added to the table: "role from group" which
display the list of "role(group)" the user has on the project.

To be able to test the membership of user to a project through
a group, "user_group_membership" data has been added to keystone
data.

Change-Id: I968bad7d285acfaf7a8ccd7458593bbcad498ce2
Partial-Bug: #1785263
This commit is contained in:
David Gutman 2018-08-03 17:01:45 +02:00
parent 610eab4ba4
commit 6e754e5dad
4 changed files with 120 additions and 8 deletions

View File

@ -97,8 +97,51 @@ class UsersTab(tabs.TableTab):
roles_list=roles
)
def _get_users_from_groups(self, project_id, roles, project_users):
"""Update with users which have role on project through a group.
:param project_id: ID of the project
:param roles: list of roles from keystone
:param project_users: list to be updated with the users found
"""
# For keystone.group_list project_id is not passed as argument because
# it is ignored when using admin credentials
# Get all groups (to be able to find group name)
groups = api.keystone.group_list(self.request)
group_names = {group.id: group.name for group in groups}
# Get a dictionary {group_id: [role_id_1, role_id_2]}
project_groups_roles = api.keystone.get_project_groups_roles(
self.request,
project=project_id)
for group_id in project_groups_roles:
group_users = api.keystone.user_list(self.request,
group=group_id)
group_roles_names = [
role.name for role in roles
if role.id in project_groups_roles[group_id]]
roles_from_group = [(role_name, group_names[group_id])
for role_name in group_roles_names]
for user in group_users:
if user.id not in project_users:
# New user: Add the user to the list
project_users[user.id] = user
project_users[user.id].roles = []
project_users[user.id].roles_from_groups = []
# Add roles from group
project_users[user.id].roles_from_groups.extend(
roles_from_group)
def get_userstable_data(self):
"""Get users with roles on the project."""
"""Get users with roles on the project.
Roles can be applied directly on the project or through a group.
"""
project_users = {}
project = self.tab_group.kwargs['project']
@ -112,6 +155,12 @@ class UsersTab(tabs.TableTab):
roles=roles,
project_users=project_users)
# Update project_users with users which have role indirectly on
# the project, (through a group)
self._get_users_from_groups(project_id=project.id,
roles=roles,
project_users=project_users)
except Exception:
exceptions.handle(self.request,
_("Unable to display the users of this project.")

View File

@ -1350,6 +1350,14 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
self.tenant.id)
self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest())
def _get_users_in_group(self, group_id):
users_in_group = [membership["user_id"] for membership in
self.user_group_membership.list()
if membership["group_id"] == group_id]
users = [user for user in self.users.list() if
user.id in users_in_group]
return users
def _project_user_roles(self, role_assignments):
roles = {}
for role_assignment in role_assignments:
@ -1358,25 +1366,45 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
role_assignment.role["id"]]
return roles
def _project_group_roles(self, role_assignments):
roles = {}
for role_assignment in role_assignments:
if hasattr(role_assignment, 'group'):
roles[role_assignment.group['id']] = [
role_assignment.role["id"]]
return roles
@test.create_mocks({api.keystone: ('tenant_get',
'user_list',
'get_project_users_roles',
'role_list',),
'get_project_groups_roles',
'role_list',
'group_list'),
quotas: ('enabled_quotas',)})
def test_detail_view_users_tab(self):
project = self.tenants.first()
users = self.users.filter(domain_id=project.domain_id)
groups = self.groups.filter(domain_id=project.domain_id)
role_assignments = self.role_assignments.filter(
scope={'project': {'id': project.id}})
# {user_id: [role_id1, role_id2]} as returned by the api
project_users_roles = self._project_user_roles(role_assignments)
# {group_id: [role_id1, role_id2]} as returned by the api
project_groups_roles = self._project_group_roles(role_assignments)
# Prepare mocks
self.mock_tenant_get.return_value = project
self.mock_enabled_quotas.return_value = ('instances',)
self.mock_role_list.return_value = self.roles.list()
self.mock_user_list.return_value = users
def _user_list_side_effect(request, group=None):
if group:
return self._get_users_in_group(group)
return users
self.mock_user_list.side_effect = _user_list_side_effect
self.mock_group_list.return_value = groups
self.mock_get_project_users_roles.return_value = project_users_roles
self.mock_get_project_groups_roles.return_value = project_groups_roles
# Get project details view on user tab
url = PROJECT_DETAIL_URL % [project.id]
@ -1392,27 +1420,40 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
# Check the content of the table
users_expected = {
'1': {'roles': ['admin'], },
'2': {'roles': ['_member_'], },
'3': {'roles': ['_member_'], },
'1': {'roles': ['admin'],
'roles_from_groups': [('_member_', 'group_one'), ], },
'2': {'roles': ['_member_'],
'roles_from_groups': [], },
'3': {'roles': ['_member_'],
'roles_from_groups': [('_member_', 'group_one'), ], },
'4': {'roles': [],
'roles_from_groups': [('_member_', 'group_one'), ], }
}
users_id_observed = [user.id for user in
res.context["userstable_table"].data]
self.assertItemsEqual(users_expected.keys(), users_id_observed)
# Check the users roles
# Check the users groups and roles
for user in res.context["userstable_table"].data:
self.assertItemsEqual(users_expected[user.id]["roles"],
user.roles)
self.assertItemsEqual(users_expected[user.id]["roles_from_groups"],
user.roles_from_groups)
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
self.tenant.id)
self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest())
self.mock_role_list.assert_called_once_with(test.IsHttpRequest())
self.mock_group_list.assert_called_once_with(test.IsHttpRequest())
self.mock_get_project_users_roles.assert_called_once_with(
test.IsHttpRequest(), project=project.id)
self.mock_user_list.assert_called_once_with(test.IsHttpRequest())
self.mock_get_project_groups_roles.assert_called_once_with(
test.IsHttpRequest(), project=project.id)
calls = [mock.call(test.IsHttpRequest()),
mock.call(test.IsHttpRequest(), group="1"), ]
self.mock_user_list.assert_has_calls(calls)
@test.create_mocks({api.keystone: ("tenant_get",
"role_list",),

View File

@ -28,6 +28,14 @@ class UsersTable(users_tables.UsersTable):
widget=forms.Textarea(attrs={'rows': 4}),
required=False))
groups_roles = tables.Column(
lambda obj: ", ".join("%s (%s)" % (role, group) for role, group in
getattr(obj, 'roles_from_groups')),
verbose_name=_('Roles from Groups'),
form_field=forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False))
class Meta(object):
name = "userstable"
verbose_name = _("Users")

View File

@ -127,6 +127,7 @@ def data(TEST):
TEST.domains = utils.TestDataContainer()
TEST.users = utils.TestDataContainer()
TEST.groups = utils.TestDataContainer()
TEST.user_group_membership = utils.TestDataContainer()
TEST.tenants = utils.TestDataContainer()
TEST.role_assignments = utils.TestDataContainer()
TEST.roles = utils.TestDataContainer()
@ -252,6 +253,19 @@ def data(TEST):
TEST.groups.add(group, group2, group3, group4, group5)
user_group_membership = {"user_id": "1", "group_id": "1"}
TEST.user_group_membership.add(user_group_membership)
user_group_membership = {"user_id": "1", "group_id": "2"}
TEST.user_group_membership.add(user_group_membership)
user_group_membership = {"user_id": "2", "group_id": "2"}
TEST.user_group_membership.add(user_group_membership)
user_group_membership = {"user_id": "2", "group_id": "3"}
TEST.user_group_membership.add(user_group_membership)
user_group_membership = {"user_id": "3", "group_id": "1"}
TEST.user_group_membership.add(user_group_membership)
user_group_membership = {"user_id": "4", "group_id": "1"}
TEST.user_group_membership.add(user_group_membership)
role_assignments_dict = {'user': {'id': '1'},
'role': {'id': '1'},
'scope': {'project': {'id': '1'}}}