Add RBAC policies feature to horizon dashboard

Add RBAC Policies panel to support Role-Based Access Control
functionality.

Implements: blueprint rbac-policies
Change-Id: I883ad629d735dadf49e8bf9c50475050fdfcf797
This commit is contained in:
shutingm 2018-11-21 14:44:30 +08:00
parent c4515d1bf3
commit d65ebe2054
20 changed files with 830 additions and 0 deletions

View File

@ -1645,6 +1645,7 @@ Default:
'enable_ha_router': False,
'enable_ipv6': True,
'enable_quotas': False,
'enable_rbac_policy': True,
'enable_router': True,
'extra_provider_types': {},
'physical_networks': [],
@ -1749,6 +1750,16 @@ Enable support for Neutron quotas feature. To make this feature work
appropriately, you need to use Neutron plugins with quotas extension support
and quota_driver should be DbQuotaDriver (default config).
enable_rbac_policy
##################
.. versionadded:: 15.0.0(Stein)
Default: ``True``
Set this to True to enable RBAC Policies panel that provide the ability for
users to use RBAC function. This option only affects when Neutron is enabled.
enable_router
#############

View File

@ -1988,3 +1988,59 @@ def list_availability_zones(request, resource=None, state=None):
az_list = [az for az in az_list if az['state'] == state]
return sorted(az_list, key=lambda zone: zone['name'])
class RBACPolicy(NeutronAPIDictWrapper):
"""Wrapper for neutron RBAC Policy."""
def rbac_policy_create(request, **kwargs):
"""Create a RBAC Policy.
:param request: request context
:param target_tenant: target tenant of the policy
:param tenant_id: owner tenant of the policy(Not recommended)
:param object_type: network or qos_policy
:param object_id: object id of policy
:param action: access_as_shared or access_as_external
:return: RBACPolicy object
"""
body = {'rbac_policy': kwargs}
rbac_policy = neutronclient(request).create_rbac_policy(
body=body).get('rbac_policy')
return RBACPolicy(rbac_policy)
def rbac_policy_list(request, **kwargs):
"""List of RBAC Policies."""
policies = neutronclient(request).list_rbac_policies(
**kwargs).get('rbac_policies')
return [RBACPolicy(p) for p in policies]
def rbac_policy_update(request, policy_id, **kwargs):
"""Update a RBAC Policy.
:param request: request context
:param policy_id: target policy id
:param target_tenant: target tenant of the policy
:return: RBACPolicy object
"""
body = {'rbac_policy': kwargs}
rbac_policy = neutronclient(request).update_rbac_policy(
policy_id, body=body).get('rbac_policy')
return RBACPolicy(rbac_policy)
@profiler.trace
def rbac_policy_get(request, policy_id, **kwargs):
"""Get RBAC policy for a given policy id."""
policy = neutronclient(request).show_rbac_policy(
policy_id, **kwargs).get('rbac_policy')
return RBACPolicy(policy)
@profiler.trace
def rbac_policy_delete(request, policy_id):
"""Delete RBAC policy for a given policy id."""
neutronclient(request).delete_rbac_policy(policy_id)

View File

@ -0,0 +1,170 @@
# Copyright 2019 vmware, Inc.
#
# 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 logging
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
# Predefined provider types.
ACTIONS = [
{
'name': 'access_as_shared',
'value': _('Access as Shared')
},
{
'name': 'access_as_external',
'value': _('Access as External')
}
]
# Predefined provider object types.
OBJECT_TYPES = [
{
'name': 'network',
'value': _('Network')
}
]
QOS_POLICY_TYPE = {
'name': 'qos_policy',
'value': _('QoS Policy')
}
class CreatePolicyForm(forms.SelfHandlingForm):
target_tenant = forms.ThemableChoiceField(label=_("Target Project"))
object_type = forms.ThemableChoiceField(
label=_("Object Type"),
widget=forms.ThemableSelectWidget(
attrs={
'class': 'switchable',
'data-slug': 'object_type'
}))
network_id = forms.ThemableChoiceField(
label=_("Network"),
widget=forms.ThemableSelectWidget(attrs={
'class': 'switched',
'data-switch-on': 'object_type',
}),
required=False)
qos_policy_id = forms.ThemableChoiceField(
label=_("QoS Policy"),
widget=forms.ThemableSelectWidget(attrs={
'class': 'switched',
'data-switch-on': 'object_type',
}),
required=False)
action = forms.ThemableChoiceField(label=_("Action"))
def __init__(self, request, *args, **kwargs):
super(CreatePolicyForm, self).__init__(request, *args, **kwargs)
tenant_choices = [('', _("Select a project"))]
tenants, has_more = api.keystone.tenant_list(request)
tenant_choices.append(("*", "*"))
for tenant in tenants:
tenant_choices.append((tenant.id, tenant.name))
self.fields['target_tenant'].choices = tenant_choices
action_choices = [('', _("Select an action"))]
for action in ACTIONS:
action_choices.append((action['name'],
action['value']))
self.fields['action'].choices = action_choices
network_choices = []
networks = api.neutron.network_list(request)
for network in networks:
network_choices.append((network.id, network.name))
self.fields['network_id'].choices = network_choices
# If enable QoS Policy
if api.neutron.is_extension_supported(request, extension_alias='qos'):
qos_policies = api.neutron.policy_list(request)
qos_choices = [(qos_policy['id'], qos_policy['name'])
for qos_policy in qos_policies]
self.fields['qos_policy_id'].choices = qos_choices
if QOS_POLICY_TYPE not in OBJECT_TYPES:
OBJECT_TYPES.append(QOS_POLICY_TYPE)
object_type_choices = [('', _("Select an object type"))]
for object_type in OBJECT_TYPES:
object_type_choices.append((object_type['name'],
object_type['value']))
self.fields['object_type'].choices = object_type_choices
# Register object types which required
self.fields['network_id'].widget.attrs.update(
{'data-object_type-network': _('Network')})
self.fields['qos_policy_id'].widget.attrs.update(
{'data-object_type-qos_policy': _('QoS Policy')})
def handle(self, request, data):
try:
params = {
'target_tenant': data['target_tenant'],
'action': data['action'],
'object_type': data['object_type'],
}
if data['object_type'] == 'network':
params['object_id'] = data['network_id']
elif data['object_type'] == 'qos_policy':
params['object_id'] = data['qos_policy_id']
rbac_policy = api.neutron.rbac_policy_create(request, **params)
msg = _('RBAC Policy was successfully created.')
messages.success(request, msg)
return rbac_policy
except Exception:
redirect = reverse('horizon:admin:rbac_policies:index')
msg = _('Failed to create a rbac policy.')
exceptions.handle(request, msg, redirect=redirect)
return False
class UpdatePolicyForm(forms.SelfHandlingForm):
target_tenant = forms.ThemableChoiceField(label=_("Target Project"))
failure_url = 'horizon:admin:rbac_policies:index'
def __init__(self, request, *args, **kwargs):
super(UpdatePolicyForm, self).__init__(request, *args, **kwargs)
tenant_choices = [('', _("Select a project"))]
tenants, has_more = api.keystone.tenant_list(request)
for tenant in tenants:
tenant_choices.append((tenant.id, tenant.name))
self.fields['target_tenant'].choices = tenant_choices
def handle(self, request, data):
try:
params = {'target_tenant': data['target_tenant']}
rbac_policy = api.neutron.rbac_policy_update(
request, self.initial['rbac_policy_id'], **params)
msg = _('RBAC Policy %s was successfully updated.') \
% self.initial['rbac_policy_id']
messages.success(request, msg)
return rbac_policy
except Exception as e:
LOG.info('Failed to update rbac policy %(id)s: %(exc)s',
{'id': self.initial['rbac_policy_id'], 'exc': e})
msg = _('Failed to update rbac policy %s') \
% self.initial['rbac_policy_id']
redirect = reverse(self.failure_url)
exceptions.handle(request, msg, redirect=redirect)

View File

@ -0,0 +1,44 @@
# 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 logging
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.api import neutron
LOG = logging.getLogger(__name__)
class RBACPolicies(horizon.Panel):
name = _("RBAC Policies")
slug = "rbac_policies"
permissions = ('openstack.services.network',)
policy_rules = (("network", "context_is_admin"),)
def allowed(self, context):
request = context['request']
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
try:
return (
network_config.get('enable_rbac_policy', True) and
neutron.is_extension_supported(request,
extension_alias='rbac-policies')
)
except Exception:
LOG.error("Call to list enabled services failed. This is likely "
"due to a problem communicating with the Neutron "
"endpoint. RBAC Policies panel will not be displayed.")
return False

View File

@ -0,0 +1,82 @@
# Copyright 2019 vmware, Inc.
#
# 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.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import tables
from openstack_dashboard import api
from openstack_dashboard import policy
class CreateRBACPolicy(policy.PolicyTargetMixin, tables.LinkAction):
name = "create"
verbose_name = _("Create RBAC Policy")
url = "horizon:admin:rbac_policies:create"
classes = ("ajax-modal",)
icon = "plus"
policy_rules = (("network", "create_rbac_policy"),)
class DeleteRBACPolicy(policy.PolicyTargetMixin, tables.DeleteAction):
help_text = _("Deleted RBAC policy is not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete RBAC Policy",
u"Delete RBAC Policies",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted RBAC Policy",
u"Deleted RBAC Policies",
count
)
policy_rules = (("network", "delete_rbac_policy"),)
def delete(self, request, obj_id):
api.neutron.rbac_policy_delete(request, obj_id)
class UpdateRBACPolicy(policy.PolicyTargetMixin, tables.LinkAction):
name = "update"
verbose_name = _("Edit Policy")
url = "horizon:admin:rbac_policies:update"
classes = ("ajax-modal",)
icon = "pencil"
policy_rules = (("network", "update_rbac_policy"),)
class RBACPoliciesTable(tables.DataTable):
tenant = tables.Column("tenant_name", verbose_name=_("Project"))
id = tables.WrappingColumn('id',
verbose_name=_('ID'),
link="horizon:admin:rbac_policies:detail")
object_type = tables.WrappingColumn('object_type',
verbose_name=_('Object Type'))
object_name = tables.Column("object_name", verbose_name=_("Object"))
target_tenant = tables.Column("target_tenant_name",
verbose_name=_("Target Project"))
class Meta(object):
name = "rbac policies"
verbose_name = _("RBAC Policies")
table_actions = (CreateRBACPolicy, DeleteRBACPolicy,)
row_actions = (UpdateRBACPolicy, DeleteRBACPolicy)

View File

@ -0,0 +1,57 @@
# Copyright 2019 vmware, Inc.
# All Rights Reserved.
#
# 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.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from horizon.utils import memoized
from openstack_dashboard import api
class OverviewTab(tabs.Tab):
name = _("Overview")
slug = "overview"
template_name = "admin/rbac_policies/_detail_overview.html"
preload = False
@memoized.memoized_method
def _get_data(self):
rbac_policy = {}
rbac_policy_id = None
try:
rbac_policy_id = self.tab_group.kwargs['rbac_policy_id']
rbac_policy = api.neutron.rbac_policy_get(self.request,
rbac_policy_id)
except Exception:
msg = _('Unable to retrieve details for rbac_policy "%s".') \
% (rbac_policy_id)
exceptions.handle(self.request, msg)
return rbac_policy
def get_context_data(self, request, **kwargs):
context = super(OverviewTab, self).get_context_data(request, **kwargs)
rbac_policy = self._get_data()
context["rbac_policy"] = rbac_policy
return context
class RBACDetailsTabs(tabs.DetailTabsGroup):
slug = "rbac_policy_tabs"
tabs = (OverviewTab, )
sticky = True

View File

@ -0,0 +1,7 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans "From here you can create a rbac policy." %}</p>
{% endblock %}

View File

@ -0,0 +1,18 @@
{% load i18n sizeformat %}
<div class="info detail">
<dl class="dl-horizontal">
<dt title="{% trans 'ID' %}">{% trans "ID" %}</dt>
<dd>{{ rbac_policy.id|default:_("None") }}</dd>
<dt title="{% trans 'Project ID' %}">{% trans "Project ID" %}</dt>
<dd>{{ rbac_policy.project_id|default:_("-") }}</dd>
<dt title="{% trans 'Object Type' %}">{% trans "Object Type" %}</dt>
<dd>{{ rbac_policy.object_type|default:_("Unknown") }}</dd>
<dt title="{% trans 'Object ID' %}">{% trans "Object ID" %}</dt>
<dd>{{ rbac_policy.object_id|default:_("Unknown") }}</dd>
<dt title="{% trans 'Action' %}">{% trans "Action" %}</dt>
<dd>{{ rbac_policy.action|default:_("Unknown") }}</dd>
<dt title="{% trans 'Target Tenant' %}">{% trans "Target Tenant" %}</dt>
<dd>{{ rbac_policy.target_tenant|default:_("None") }}</dd>
</dl>
</div>

View File

@ -0,0 +1,7 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans "You may update the editable properties of the RBAC policy here." %}</p>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create a RBAC Policy" %}{% endblock %}
{% block main %}
{% include 'admin/rbac_policies/_create.html' %}
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "RBAC Policy Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_detail_header.html" %}
{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
<hr>
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update RBAC Policy" %}{% endblock %}
{% block main %}
{% include 'project/rbac_policies/_update.html' %}
{% endblock %}

View File

@ -0,0 +1,122 @@
# Copyright 2019 vmware, Inc.
#
# 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.urls import reverse
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
INDEX_URL = reverse('horizon:admin:rbac_policies:index')
class RBACPolicyTests(test.BaseAdminViewTests):
@test.create_mocks({api.neutron: ('rbac_policy_list',
'network_list',
'policy_list',
'is_extension_supported',),
api.keystone: ('tenant_list',)})
def test_index(self):
tenants = self.tenants.list()
self.mock_tenant_list.return_value = [tenants, False]
self.mock_network_list.return_value = self.networks.list()
self.mock_policy_list.return_value = self.qos_policies.list()
self.mock_rbac_policy_list.return_value = self.rbac_policies.list()
self.mock_is_extension_supported.return_value = True
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
rbac_policies = res.context['table'].data
self.assertItemsEqual(rbac_policies, self.rbac_policies.list())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest())
self.mock_policy_list.assert_called_once_with(test.IsHttpRequest())
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), extension_alias='qos')
self.mock_rbac_policy_list.assert_called_once_with(
test.IsHttpRequest())
@test.create_mocks({api.neutron: ('network_list',
'rbac_policy_create',
'is_extension_supported',),
api.keystone: ('tenant_list',)})
def test_rbac_create_post_with_network_type(self):
network = self.networks.first()
tenants = self.tenants.list()
rbac_policy = self.rbac_policies.first()
self.mock_tenant_list.return_value = [tenants, False]
self.mock_network_list.return_value = self.networks.list()
self.mock_is_extension_supported.return_value = False
self.mock_rbac_policy_create.return_value = rbac_policy
form_data = {'target_tenant': rbac_policy.target_tenant,
'action': 'access_as_external',
'object_type': 'network',
'network_id': network.id}
url = reverse('horizon:admin:rbac_policies:create')
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest())
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), extension_alias='qos')
params = {'target_tenant': rbac_policy.target_tenant,
'action': 'access_as_external',
'object_type': 'network',
'object_id': network.id}
self.mock_rbac_policy_create.assert_called_once_with(
test.IsHttpRequest(), **params)
@test.create_mocks({api.neutron: ('network_list',
'policy_list',
'rbac_policy_create',
'is_extension_supported',),
api.keystone: ('tenant_list',)})
def test_rbac_create_post_with_qos_policy_type(self):
qos_policy = self.qos_policies.first()
tenants = self.tenants.list()
rbac_policy = self.rbac_policies.filter(object_type="qos_policy")[0]
self.mock_tenant_list.return_value = [tenants, False]
self.mock_network_list.return_value = self.networks.list()
self.mock_policy_list.return_value = self.qos_policies.list()
self.mock_is_extension_supported.return_value = True
self.mock_rbac_policy_create.return_value = rbac_policy
form_data = {'target_tenant': rbac_policy.target_tenant,
'action': 'access_as_shared',
'object_type': 'qos_policy',
'qos_policy_id': qos_policy.id}
url = reverse('horizon:admin:rbac_policies:create')
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest())
self.mock_policy_list.assert_called_once_with(test.IsHttpRequest())
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), extension_alias='qos')
params = {'target_tenant': rbac_policy.target_tenant,
'action': 'access_as_shared',
'object_type': 'qos_policy',
'object_id': qos_policy.id}
self.mock_rbac_policy_create.assert_called_once_with(
test.IsHttpRequest(), **params)

View File

@ -0,0 +1,30 @@
# 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.conf.urls import url
from openstack_dashboard.dashboards.admin.rbac_policies import views
RBAC_POLICY_URL = r'^(?P<rbac_policy_id>[^/]+)/%s$'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^create/$', views.CreateView.as_view(), name='create'),
url(RBAC_POLICY_URL % '$',
views.DetailView.as_view(),
name='detail'),
url(RBAC_POLICY_URL % 'update',
views.UpdateView.as_view(),
name='update'),
]

View File

@ -0,0 +1,144 @@
# 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 collections import OrderedDict
from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from openstack_dashboard import api
from openstack_dashboard.dashboards.admin.rbac_policies \
import forms as rbac_policy_forms
from openstack_dashboard.dashboards.admin.rbac_policies \
import tables as rbac_policy_tables
from openstack_dashboard.dashboards.admin.rbac_policies \
import tabs as rbac_policy_tabs
class IndexView(tables.DataTableView):
table_class = rbac_policy_tables.RBACPoliciesTable
page_title = _("RBAC Policies")
@memoized.memoized_method
def _get_tenants(self):
try:
tenants, has_more = api.keystone.tenant_list(self.request)
except Exception:
tenants = []
msg = _("Unable to retrieve information about the "
"policies' projects.")
exceptions.handle(self.request, msg)
tenant_dict = OrderedDict([(t.id, t.name) for t in tenants])
return tenant_dict
def _get_networks(self):
try:
networks = api.neutron.network_list(self.request)
except Exception:
networks = []
msg = _("Unable to retrieve information about the "
"policies' networks.")
exceptions.handle(self.request, msg)
return dict([(n.id, n.name) for n in networks])
def _get_qos_policies(self):
qos_policies = []
try:
if api.neutron.is_extension_supported(self.request,
extension_alias='qos'):
qos_policies = api.neutron.policy_list(self.request)
except Exception:
msg = _("Unable to retrieve information about the "
"policies' qos policies.")
exceptions.handle(self.request, msg)
return dict([(q.id, q.name) for q in qos_policies])
def get_data(self):
try:
rbac_policies = api.neutron.rbac_policy_list(self.request)
except Exception:
rbac_policies = []
messages.error(self.request,
_("Unable to retrieve RBAC policies."))
if rbac_policies:
tenant_dict = self._get_tenants()
network_dict = self._get_networks()
qos_policy_dict = self._get_qos_policies()
for p in rbac_policies:
# Set tenant name and object name
p.tenant_name = tenant_dict.get(p.tenant_id, p.tenant_id)
p.target_tenant_name = tenant_dict.get(p.target_tenant,
p.target_tenant)
if p.object_type == "network":
p.object_name = network_dict.get(p.object_id, p.object_id)
elif p.object_type == "qos_policy":
p.object_name = qos_policy_dict.get(p.object_id,
p.object_id)
return rbac_policies
class CreateView(forms.ModalFormView):
template_name = 'admin/rbac_policies/create.html'
form_id = "create_rbac_policy_form"
form_class = rbac_policy_forms.CreatePolicyForm
submit_label = _("Create RBAC Policy")
submit_url = reverse_lazy("horizon:admin:rbac_policies:create")
success_url = reverse_lazy("horizon:admin:rbac_policies:index")
page_title = _("Create A RBAC Policy")
class UpdateView(forms.ModalFormView):
context_object_name = 'rbac_policies'
template_name = 'admin/rbac_policies/update.html'
form_class = rbac_policy_forms.UpdatePolicyForm
form_id = "update_rbac_policy_form"
submit_label = _("Save Changes")
submit_url = 'horizon:admin:rbac_policies:update'
success_url = reverse_lazy('horizon:admin:rbac_policies:index')
page_title = _("Update RBAC Policy")
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
args = (self.kwargs['rbac_policy_id'],)
context["rbac_policy_id"] = self.kwargs['rbac_policy_id']
context["submit_url"] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def _get_object(self, *args, **kwargs):
rbac_policy_id = self.kwargs['rbac_policy_id']
try:
return api.neutron.rbac_policy_get(self.request, rbac_policy_id)
except Exception:
redirect = self.success_url
msg = _('Unable to retrieve rbac policy details.')
exceptions.handle(self.request, msg, redirect=redirect)
def get_initial(self):
rbac_policy = self._get_object()
return {'rbac_policy_id': rbac_policy['id'],
'target_tenant': rbac_policy['target_tenant']}
class DetailView(tabs.TabView):
tab_group_class = rbac_policy_tabs.RBACDetailsTabs
template_name = 'horizon/common/_detail.html'
page_title = "{{ rbac_policy.id }}"

View File

@ -0,0 +1,10 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'rbac_policies'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'network'
# Python panel class of the PANEL to be added.
ADD_PANEL = ('openstack_dashboard.dashboards.admin.'
'rbac_policies.panel.RBACPolicies')

View File

@ -300,6 +300,11 @@ TEST_GLOBAL_MOCKS_ON_PANELS = {
'.network_qos.panel.NetworkQoS.can_access'),
'return_value': True,
},
'rbac_policies': {
'method': ('openstack_dashboard.dashboards.admin'
'.rbac_policies.panel.RBACPolicies.can_access'),
'return_value': True,
},
'server_groups': {
'method': ('openstack_dashboard.dashboards.project'
'.server_groups.panel.ServerGroups.can_access'),

View File

@ -45,6 +45,7 @@ def data(TEST):
TEST.neutron_quota_usages = utils.TestDataContainer()
TEST.ip_availability = utils.TestDataContainer()
TEST.qos_policies = utils.TestDataContainer()
TEST.rbac_policies = utils.TestDataContainer()
TEST.tp_ports = utils.TestDataContainer()
TEST.neutron_availability_zones = utils.TestDataContainer()
@ -66,6 +67,7 @@ def data(TEST):
TEST.api_monitors = utils.TestDataContainer()
TEST.api_extensions = utils.TestDataContainer()
TEST.api_ip_availability = utils.TestDataContainer()
TEST.api_rbac_policies = utils.TestDataContainer()
TEST.api_qos_policies = utils.TestDataContainer()
TEST.api_tp_trunks = utils.TestDataContainer()
TEST.api_tp_ports = utils.TestDataContainer()
@ -773,6 +775,26 @@ def data(TEST):
TEST.api_qos_policies.add(policy_dict1)
TEST.qos_policies.add(neutron.QoSPolicy(policy_dict1))
# rbac policies
rbac_policy_dict = {"project_id": "1",
"object_type": "network",
"id": "7f27e61a-9863-448a-a769-eb922fdef3f8",
"object_id": "82288d84-e0a5-42ac-95be-e6af08727e42",
"target_tenant": "2",
"action": "access_as_external",
"tenant_id": "1"}
TEST.api_rbac_policies.add(rbac_policy_dict)
TEST.rbac_policies.add(neutron.RBACPolicy(rbac_policy_dict))
rbac_policy_dict1 = {"project_id": "1",
"object_type": "qos_policy",
"id": "7f27e61a-9863-448a-a769-eb922fdef3f8",
"object_id": "a21dcd22-7189-cccc-aa32-22adafaf16a7",
"target_tenant": "2",
"action": "access_as_shared",
"tenant_id": "1"}
TEST.api_rbac_policies.add(rbac_policy_dict1)
TEST.rbac_policies.add(neutron.RBACPolicy(rbac_policy_dict1))
# TRUNKPORT
#
# The test setup was created by the following command sequence:

View File

@ -0,0 +1,11 @@
---
features:
- >
[`blueprint neutron-rbac-policies <https://blueprints.launchpad.net/horizon/+spec/rbac-policies>`_]
This blueprint adds RBAC policies panel to the Admin Network group.
This panel will be enabled by default when the RBAC extension is
enabled. Remove this panel by setting "'enable_rbac_policy': False"
in 'local_settings.py'. RBAC policy supports the control of two
resources: networks and qos policies, because qos policies is
an extension function of neutron, need to enable this extension
if wants to use it.