Add role assignment tab in the user details view.

Add an extra tab "role assignments" in the user details view
which displays all the project/role domain/role of the current
user.

The role assignments are displayed in a table.
The project/domain column is clickable and forward to the project
/domain detail view.

Change-Id: Iefe17856e60b84d089f722c3a30e9ede21d8ce47
Partial-Bug: #1792524
Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
This commit is contained in:
David Gutman 2018-09-13 15:07:13 +02:00 committed by Akihiro Motoki
parent 254e3791d3
commit ba82055f05
4 changed files with 216 additions and 1 deletions

View File

@ -0,0 +1,93 @@
# 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.
from django import urls
from django.utils.translation import ugettext_lazy as _
from horizon import forms
from horizon import tables
from openstack_dashboard import policy
def get_project_name(datum):
if "project" in datum.scope:
if "name" in datum.scope["project"]:
return datum.scope["project"]["name"]
return datum.scope["project"]["id"]
def get_project_link(datum, request):
if "project" not in datum.scope:
return
if policy.check((("identity", "identity:get_project"),),
request, target={"project": datum}):
return urls.reverse("horizon:identity:projects:detail",
args=(datum.scope["project"]["id"],))
def get_domain_name(datum):
if "domain" in datum.scope:
if "name" in datum.scope["domain"]:
return datum.scope["domain"]["name"]
return datum.scope["domain"]["id"]
def get_system_scope(datum):
if "system" in datum.scope:
return ', '.join(datum.scope["system"].keys())
def get_role_name(datum):
if "name" in datum.role:
return datum.role["name"]
return datum.role["id"]
class RoleAssignmentsTable(tables.DataTable):
project = tables.WrappingColumn(get_project_name,
verbose_name=_('Project'),
link=get_project_link,
form_field=forms.CharField(max_length=64))
domain = tables.WrappingColumn(get_domain_name,
verbose_name=_('Domain'),
form_field=forms.CharField(max_length=64))
system = tables.WrappingColumn(get_system_scope,
verbose_name=_('System Scope'),
form_field=forms.CharField(max_length=64))
role = tables.Column(get_role_name,
verbose_name=_('Role'),
form_field=forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False))
def get_object_id(self, datum):
"""Identifier of the role assignment."""
# Role assignment doesn't have identifier so one will be created
# from the identifier of scope, user and role. This will guaranty the
# unicity.
scope_id = ""
if "project" in datum.scope:
scope_id = datum.scope["project"]["id"]
elif "domain" in datum.scope:
scope_id = datum.scope["domain"]["id"]
return "%s%s%s" % (datum.user["id"], datum.role["id"], scope_id)
class Meta(object):
name = "roleassignmentstable"
verbose_name = _("Role assignments")

View File

@ -13,8 +13,13 @@
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from openstack_dashboard import api
from openstack_dashboard.dashboards.identity.users.role_assignments \
import tables as role_assignments_tables
class OverviewTab(tabs.Tab):
"""Overview of the user.
@ -29,6 +34,33 @@ class OverviewTab(tabs.Tab):
return {"user": self.tab_group.kwargs['user']}
class RoleAssignmentsTab(tabs.TableTab):
"""Role assignment of the user to domain/project."""
table_classes = (role_assignments_tables.RoleAssignmentsTable,)
name = _("Role assignments")
slug = "roleassignments"
template_name = "horizon/common/_detail_table.html"
preload = False
def get_roleassignmentstable_data(self):
user = self.tab_group.kwargs['user']
try:
# Get all the roles of the user
user_roles = api.keystone.role_assignments_list(
self.request, user=user, include_subtree=False,
include_names=True)
return user_roles
except Exception:
exceptions.handle(
self.request,
_("Unable to display the role assignments of this user."))
return []
class UserDetailTabs(tabs.DetailTabsGroup):
slug = "user_details"
tabs = (OverviewTab,)
tabs = (OverviewTab, RoleAssignmentsTab,)

View File

@ -922,6 +922,96 @@ class UsersViewTests(test.BaseAdminViewTests):
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
user.project_id)
@test.create_mocks({api.keystone: ('domain_get',
'user_get',
'tenant_get',
'role_assignments_list')})
def test_detail_view_role_assignments_tab(self):
"""Test the role assignments tab of the detail view ."""
domain = self._get_default_domain()
user = self.users.get(id="1")
tenant = self.tenants.get(id=user.project_id)
role_assignments = self.role_assignments.filter(user={'id': user.id})
self.mock_domain_get.return_value = domain
self.mock_user_get.return_value = user
self.mock_tenant_get.return_value = tenant
self.mock_role_assignments_list.return_value = role_assignments
# Url of the role assignment tab of the detail view
url = USER_DETAIL_URL % [user.id]
detail_view = tabs.UserDetailTabs(self.request, user=user)
role_assignments_tab_link = "?%s=%s" % (
detail_view.param_name,
detail_view.get_tab("roleassignments").get_id()
)
url += role_assignments_tab_link
res = self.client.get(url)
# Check the template expected has been used
self.assertTemplateUsed(res,
"horizon/common/_detail_table.html")
# Check the table contains the expected data
role_assignments_expected = role_assignments
role_assignments_observed = res.context["table"].data
self.assertItemsEqual(role_assignments_expected,
role_assignments_observed)
self.mock_domain_get.assert_called_once_with(test.IsHttpRequest(), '1')
self.mock_user_get.assert_called_once_with(test.IsHttpRequest(), '1',
admin=False)
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
user.project_id)
self.mock_role_assignments_list.assert_called_once_with(
test.IsHttpRequest(), user=user, include_subtree=False,
include_names=True)
@test.create_mocks({api.keystone: ('domain_get',
'user_get',
'tenant_get',
'role_assignments_list')})
def test_detail_view_role_assignments_tab_with_exception(self):
"""Test the role assignments tab with exception.
The table is displayed empty and an error message pop if the role
assignment request fails.
"""
domain = self._get_default_domain()
user = self.users.get(id="1")
tenant = self.tenants.get(id=user.project_id)
self.mock_domain_get.return_value = domain
self.mock_user_get.return_value = user
self.mock_tenant_get.return_value = tenant
self.mock_role_assignments_list.side_effect = self.exceptions.keystone
# Url of the role assignment tab of the detail view
url = USER_DETAIL_URL % [user.id]
detail_view = tabs.UserDetailTabs(self.request, user=user)
role_assignments_tab_link = "?%s=%s" % (
detail_view.param_name,
detail_view.get_tab("roleassignments").get_id()
)
url += role_assignments_tab_link
res = self.client.get(url)
# Check the role assignment table is empty
self.assertEqual(res.context["table"].data, [])
# Check one error message is displayed
self.assertMessageCount(res, error=1)
self.mock_domain_get.assert_called_once_with(test.IsHttpRequest(), '1')
self.mock_user_get.assert_called_once_with(test.IsHttpRequest(), '1',
admin=False)
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
user.project_id)
self.mock_role_assignments_list.assert_called_once_with(
test.IsHttpRequest(), user=user, include_subtree=False,
include_names=True)
@test.create_mocks({api.keystone: ('user_get',
'domain_get',
'tenant_list',)})