Separating Identity Dashboard and using RBAC
Moving identity panels to their own dashboard. RBAC is now used to determine the data to load in the identity dashboard. Using the default policy file, a user with role member will now be able to see their project list. Also, adding a policy check mechanism at the panel and dashboard level to determine which panels and dashboards the user can access. Implements blueprint separate-identity-dash Change-Id: I7ebfec2bf6e44899bec79d3b23c90d56a976200f
This commit is contained in:
parent
8863146bb0
commit
18e8ea810d
@ -61,6 +61,8 @@ class NotRegistered(Exception):
|
||||
|
||||
|
||||
class HorizonComponent(object):
|
||||
policy_rules = None
|
||||
|
||||
def __init__(self):
|
||||
super(HorizonComponent, self).__init__()
|
||||
if not self.slug:
|
||||
@ -88,6 +90,29 @@ class HorizonComponent(object):
|
||||
urlpatterns = patterns('')
|
||||
return urlpatterns
|
||||
|
||||
def can_access(self, context):
|
||||
"""Checks to see that the user has role based access to this component.
|
||||
|
||||
This method should be overridden to return the result of
|
||||
any policy checks required for the user to access this component
|
||||
when more complex checks are required.
|
||||
"""
|
||||
return self._can_access(context['request'])
|
||||
|
||||
def _can_access(self, request):
|
||||
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None)
|
||||
|
||||
# this check is an OR check rather than an AND check that is the
|
||||
# default in the policy engine, so calling each rule individually
|
||||
if policy_check and self.policy_rules:
|
||||
for rule in self.policy_rules:
|
||||
if policy_check((rule,), request):
|
||||
return True
|
||||
return False
|
||||
|
||||
# default to allowed
|
||||
return True
|
||||
|
||||
|
||||
class Registry(object):
|
||||
def __init__(self):
|
||||
@ -543,6 +568,30 @@ class Dashboard(Registry, HorizonComponent):
|
||||
del loaders.panel_template_dirs[key]
|
||||
return success
|
||||
|
||||
def can_access(self, context):
|
||||
"""Checks for role based access for this dashboard.
|
||||
|
||||
Checks for access to any panels in the dashboard and of the the
|
||||
dashboard itself.
|
||||
|
||||
This method should be overridden to return the result of
|
||||
any policy checks required for the user to access this dashboard
|
||||
when more complex checks are required.
|
||||
"""
|
||||
|
||||
# if the dashboard has policy rules, honor those above individual
|
||||
# panels
|
||||
if not self._can_access(context['request']):
|
||||
return False
|
||||
|
||||
# check if access is allowed to a single panel,
|
||||
# the default for each panel is True
|
||||
for panel in self.get_panels():
|
||||
if panel.can_access(context):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Workflow(object):
|
||||
def __init__(*args, **kwargs):
|
||||
|
@ -3,12 +3,16 @@
|
||||
{% for heading, panels in components.iteritems %}
|
||||
{% with panels|has_permissions_on_list:user as filtered_panels %}
|
||||
{% if filtered_panels %}
|
||||
{% if heading %}<h4>{{ heading }}</h4>{% endif %}
|
||||
{% if accessible_panels %}
|
||||
{% if heading %}<h4>{{ heading }}</h4>{% endif %}
|
||||
{% endif %}
|
||||
<ul class="main_nav">
|
||||
{% for panel in filtered_panels %}
|
||||
<li>
|
||||
<a href="{{ panel.get_absolute_url }}" {% if current == panel.slug %}class="active"{% endif %} tabindex='1'>{{ panel.name }}</a>
|
||||
</li>
|
||||
{% if panel in accessible_panels or current == panel.slug %}
|
||||
<li>
|
||||
<a href="{{ panel.get_absolute_url }}" {% if current == panel.slug %}class="active"{% endif %} tabindex='1'>{{ panel.name }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
@ -53,15 +53,19 @@ def horizon_nav(context):
|
||||
for group in panel_groups.values():
|
||||
allowed_panels = []
|
||||
for panel in group:
|
||||
if callable(panel.nav) and panel.nav(context):
|
||||
if callable(panel.nav) and panel.nav(context) and \
|
||||
panel.can_access(context):
|
||||
allowed_panels.append(panel)
|
||||
elif not callable(panel.nav) and panel.nav:
|
||||
elif not callable(panel.nav) and panel.nav and \
|
||||
panel.can_access(context):
|
||||
allowed_panels.append(panel)
|
||||
if allowed_panels:
|
||||
non_empty_groups.append((group.name, allowed_panels))
|
||||
if callable(dash.nav) and dash.nav(context):
|
||||
if callable(dash.nav) and dash.nav(context) and \
|
||||
dash.can_access(context):
|
||||
dashboards.append((dash, SortedDict(non_empty_groups)))
|
||||
elif not callable(dash.nav) and dash.nav:
|
||||
elif not callable(dash.nav) and dash.nav and \
|
||||
dash.can_access(context):
|
||||
dashboards.append((dash, SortedDict(non_empty_groups)))
|
||||
return {'components': dashboards,
|
||||
'user': context['request'].user,
|
||||
@ -78,10 +82,11 @@ def horizon_main_nav(context):
|
||||
current_dashboard = context['request'].horizon.get('dashboard', None)
|
||||
dashboards = []
|
||||
for dash in Horizon.get_dashboards():
|
||||
if callable(dash.nav) and dash.nav(context):
|
||||
dashboards.append(dash)
|
||||
elif dash.nav:
|
||||
dashboards.append(dash)
|
||||
if dash.can_access(context['request']):
|
||||
if callable(dash.nav) and dash.nav(context):
|
||||
dashboards.append(dash)
|
||||
elif dash.nav:
|
||||
dashboards.append(dash)
|
||||
return {'components': dashboards,
|
||||
'user': context['request'].user,
|
||||
'current': current_dashboard,
|
||||
@ -100,9 +105,11 @@ def horizon_dashboard_nav(context):
|
||||
for group in panel_groups.values():
|
||||
allowed_panels = []
|
||||
for panel in group:
|
||||
if callable(panel.nav) and panel.nav(context):
|
||||
if callable(panel.nav) and panel.nav(context) and \
|
||||
panel.can_access(context):
|
||||
allowed_panels.append(panel)
|
||||
elif not callable(panel.nav) and panel.nav:
|
||||
elif not callable(panel.nav) and panel.nav and \
|
||||
panel.can_access(context):
|
||||
allowed_panels.append(panel)
|
||||
if allowed_panels:
|
||||
non_empty_groups.append((group.name, allowed_panels))
|
||||
|
@ -53,6 +53,19 @@ class AdminPanel(horizon.Panel):
|
||||
urls = 'horizon.test.test_dashboards.cats.kittens.urls'
|
||||
|
||||
|
||||
class RbacNoAccessPanel(horizon.Panel):
|
||||
name = "RBAC Panel No"
|
||||
slug = "rbac_panel_no"
|
||||
|
||||
def _can_access(self, request):
|
||||
return False
|
||||
|
||||
|
||||
class RbacYesAccessPanel(horizon.Panel):
|
||||
name = "RBAC Panel Yes"
|
||||
slug = "rbac_panel_yes"
|
||||
|
||||
|
||||
class BaseHorizonTests(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -439,3 +452,67 @@ class CustomPermissionsTests(BaseHorizonTests):
|
||||
follow=False,
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
class RbacHorizonTests(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RbacHorizonTests, self).setUp()
|
||||
# Adjust our horizon config and register our custom dashboards/panels.
|
||||
self.old_default_dash = settings.HORIZON_CONFIG['default_dashboard']
|
||||
settings.HORIZON_CONFIG['default_dashboard'] = 'cats'
|
||||
self.old_dashboards = settings.HORIZON_CONFIG['dashboards']
|
||||
settings.HORIZON_CONFIG['dashboards'] = ('cats', 'dogs')
|
||||
base.Horizon.register(Cats)
|
||||
base.Horizon.register(Dogs)
|
||||
Cats.register(RbacNoAccessPanel)
|
||||
Cats.default_panel = 'rbac_panel_no'
|
||||
Dogs.register(RbacYesAccessPanel)
|
||||
Dogs.default_panel = 'rbac_panel_yes'
|
||||
# Trigger discovery, registration, and URLconf generation if it
|
||||
# hasn't happened yet.
|
||||
base.Horizon._urls()
|
||||
# Store our original dashboards
|
||||
self._discovered_dashboards = base.Horizon._registry.keys()
|
||||
# Gather up and store our original panels for each dashboard
|
||||
self._discovered_panels = {}
|
||||
for dash in self._discovered_dashboards:
|
||||
panels = base.Horizon._registry[dash]._registry.keys()
|
||||
self._discovered_panels[dash] = panels
|
||||
|
||||
def tearDown(self):
|
||||
super(RbacHorizonTests, self).tearDown()
|
||||
# Restore our settings
|
||||
settings.HORIZON_CONFIG['default_dashboard'] = self.old_default_dash
|
||||
settings.HORIZON_CONFIG['dashboards'] = self.old_dashboards
|
||||
# Destroy our singleton and re-create it.
|
||||
base.HorizonSite._instance = None
|
||||
del base.Horizon
|
||||
base.Horizon = base.HorizonSite()
|
||||
# Reload the convenience references to Horizon stored in __init__
|
||||
reload(import_module("horizon"))
|
||||
# Re-register our original dashboards and panels.
|
||||
# This is necessary because autodiscovery only works on the first
|
||||
# import, and calling reload introduces innumerable additional
|
||||
# problems. Manual re-registration is the only good way for testing.
|
||||
self._discovered_dashboards.remove(Cats)
|
||||
self._discovered_dashboards.remove(Dogs)
|
||||
for dash in self._discovered_dashboards:
|
||||
base.Horizon.register(dash)
|
||||
for panel in self._discovered_panels[dash]:
|
||||
dash.register(panel)
|
||||
|
||||
def test_rbac_panels(self):
|
||||
context = {'request': None}
|
||||
cats = horizon.get_dashboard("cats")
|
||||
self.assertEqual(cats._registered_with, base.Horizon)
|
||||
self.assertQuerysetEqual(cats.get_panels(),
|
||||
['<Panel: rbac_panel_no>'])
|
||||
self.assertFalse(cats.can_access(context))
|
||||
|
||||
dogs = horizon.get_dashboard("dogs")
|
||||
self.assertEqual(dogs._registered_with, base.Horizon)
|
||||
self.assertQuerysetEqual(dogs.get_panels(),
|
||||
['<Panel: rbac_panel_yes>'])
|
||||
|
||||
self.assertTrue(dogs.can_access(context))
|
||||
|
@ -252,8 +252,9 @@ def tenant_delete(request, project):
|
||||
return manager.delete(project)
|
||||
|
||||
|
||||
def tenant_list(request, paginate=False, marker=None, domain=None, user=None):
|
||||
manager = VERSIONS.get_project_manager(request, admin=True)
|
||||
def tenant_list(request, paginate=False, marker=None, domain=None, user=None,
|
||||
admin=True):
|
||||
manager = VERSIONS.get_project_manager(request, admin=admin)
|
||||
page_size = utils.get_page_size(request)
|
||||
|
||||
limit = None
|
||||
|
@ -25,16 +25,10 @@ class SystemPanels(horizon.PanelGroup):
|
||||
'networks', 'routers', 'info')
|
||||
|
||||
|
||||
class IdentityPanels(horizon.PanelGroup):
|
||||
slug = "identity"
|
||||
name = _("Identity")
|
||||
panels = ('domains', 'projects', 'users', 'groups', 'roles')
|
||||
|
||||
|
||||
class Admin(horizon.Dashboard):
|
||||
name = _("Admin")
|
||||
slug = "admin"
|
||||
panels = (SystemPanels, IdentityPanels)
|
||||
panels = (SystemPanels,)
|
||||
default_panel = 'overview'
|
||||
permissions = ('openstack.roles.admin',)
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
# 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.
|
||||
|
||||
GROUPS_INDEX_URL = 'horizon:admin:groups:index'
|
||||
GROUPS_INDEX_VIEW_TEMPLATE = 'admin/groups/index.html'
|
||||
GROUPS_CREATE_URL = 'horizon:admin:groups:create'
|
||||
GROUPS_CREATE_VIEW_TEMPLATE = 'admin/groups/create.html'
|
||||
GROUPS_UPDATE_URL = 'horizon:admin:groups:update'
|
||||
GROUPS_UPDATE_VIEW_TEMPLATE = 'admin/groups/update.html'
|
||||
GROUPS_MANAGE_URL = 'horizon:admin:groups:manage_members'
|
||||
GROUPS_MANAGE_VIEW_TEMPLATE = 'admin/groups/manage.html'
|
||||
GROUPS_ADD_MEMBER_URL = 'horizon:admin:groups:add_members'
|
||||
GROUPS_ADD_MEMBER_VIEW_TEMPLATE = 'admin/groups/add_non_member.html'
|
||||
GROUPS_ADD_MEMBER_AJAX_VIEW_TEMPLATE = 'admin/groups/_add_non_member.html'
|
28
openstack_dashboard/dashboards/identity/dashboard.py
Normal file
28
openstack_dashboard/dashboards/identity/dashboard.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Identity(horizon.Dashboard):
|
||||
name = _("Identity")
|
||||
slug = "identity"
|
||||
default_panel = 'projects'
|
||||
panels = ('domains', 'projects', 'users', 'groups', 'roles',)
|
||||
|
||||
|
||||
horizon.register(Identity)
|
@ -15,8 +15,8 @@
|
||||
DOMAIN_INFO_FIELDS = ("name",
|
||||
"description",
|
||||
"enabled")
|
||||
DOMAINS_INDEX_URL = 'horizon:admin:domains:index'
|
||||
DOMAINS_INDEX_VIEW_TEMPLATE = 'admin/domains/index.html'
|
||||
DOMAINS_CREATE_URL = 'horizon:admin:domains:create'
|
||||
DOMAINS_UPDATE_URL = 'horizon:admin:domains:update'
|
||||
DOMAINS_INDEX_URL = 'horizon:identity:domains:index'
|
||||
DOMAINS_INDEX_VIEW_TEMPLATE = 'identity/domains/index.html'
|
||||
DOMAINS_CREATE_URL = 'horizon:identity:domains:create'
|
||||
DOMAINS_UPDATE_URL = 'horizon:identity:domains:update'
|
||||
DOMAIN_GROUP_MEMBER_SLUG = "update_group_members"
|
@ -17,13 +17,15 @@ from django.utils.translation import ugettext_lazy as _
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
from openstack_dashboard.dashboards.identity import dashboard
|
||||
|
||||
|
||||
class Domains(horizon.Panel):
|
||||
name = _("Domains")
|
||||
slug = 'domains'
|
||||
policy_rules = (("identity", "identity:get_domain"),
|
||||
("identity", "identity:list_domains"))
|
||||
|
||||
|
||||
if keystone.VERSIONS.active >= 3:
|
||||
dashboard.Admin.register(Domains)
|
||||
dashboard.Identity.register(Domains)
|
@ -26,7 +26,7 @@ from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.admin.domains import constants
|
||||
from openstack_dashboard.dashboards.identity.domains import constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -35,7 +35,7 @@ LOG = logging.getLogger(__name__)
|
||||
class ViewGroupsLink(tables.LinkAction):
|
||||
name = "groups"
|
||||
verbose_name = _("Modify Groups")
|
||||
url = "horizon:admin:domains:update"
|
||||
url = "horizon:identity:domains:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
@ -24,8 +24,8 @@ from horizon.workflows import views
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
from openstack_dashboard.dashboards.admin.domains import constants
|
||||
from openstack_dashboard.dashboards.admin.domains import workflows
|
||||
from openstack_dashboard.dashboards.identity.domains import constants
|
||||
from openstack_dashboard.dashboards.identity.domains import workflows
|
||||
|
||||
|
||||
DOMAINS_INDEX_URL = reverse(constants.DOMAINS_INDEX_URL)
|
||||
@ -134,7 +134,7 @@ class CreateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
return domain_info
|
||||
|
||||
def test_add_domain_get(self):
|
||||
url = reverse('horizon:admin:domains:create')
|
||||
url = reverse('horizon:identity:domains:create')
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
@ -15,7 +15,7 @@
|
||||
from django.conf.urls import patterns # noqa
|
||||
from django.conf.urls import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.admin.domains import views
|
||||
from openstack_dashboard.dashboards.identity.domains import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
@ -16,15 +16,17 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from openstack_dashboard.dashboards.admin.domains import constants
|
||||
from openstack_dashboard.dashboards.admin.domains \
|
||||
from openstack_dashboard.dashboards.identity.domains import constants
|
||||
from openstack_dashboard.dashboards.identity.domains \
|
||||
import tables as project_tables
|
||||
from openstack_dashboard.dashboards.admin.domains \
|
||||
from openstack_dashboard.dashboards.identity.domains \
|
||||
import workflows as project_workflows
|
||||
|
||||
|
||||
@ -35,16 +37,30 @@ class IndexView(tables.DataTableView):
|
||||
def get_data(self):
|
||||
domains = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
try:
|
||||
if domain_context:
|
||||
if policy.check((("identity", "identity:list_domains"),),
|
||||
self.request):
|
||||
try:
|
||||
if domain_context:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
domain_context)
|
||||
domains.append(domain)
|
||||
else:
|
||||
domains = api.keystone.domain_list(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve domain list.'))
|
||||
elif policy.check((("identity", "identity:get_domain"),),
|
||||
self.request):
|
||||
try:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
domain_context)
|
||||
self.request.user.domain_id)
|
||||
domains.append(domain)
|
||||
else:
|
||||
domains = api.keystone.domain_list(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve domain list.'))
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve domain information.'))
|
||||
else:
|
||||
msg = _("Insufficient privilege level to view domain information.")
|
||||
messages.info(self.request, msg)
|
||||
return domains
|
||||
|
||||
|
@ -24,7 +24,7 @@ from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.admin.domains import constants
|
||||
from openstack_dashboard.dashboards.identity.domains import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
23
openstack_dashboard/dashboards/identity/groups/constants.py
Normal file
23
openstack_dashboard/dashboards/identity/groups/constants.py
Normal file
@ -0,0 +1,23 @@
|
||||
# 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.
|
||||
|
||||
GROUPS_INDEX_URL = 'horizon:identity:groups:index'
|
||||
GROUPS_INDEX_VIEW_TEMPLATE = 'identity/groups/index.html'
|
||||
GROUPS_CREATE_URL = 'horizon:identity:groups:create'
|
||||
GROUPS_CREATE_VIEW_TEMPLATE = 'identity/groups/create.html'
|
||||
GROUPS_UPDATE_URL = 'horizon:identity:groups:update'
|
||||
GROUPS_UPDATE_VIEW_TEMPLATE = 'identity/groups/update.html'
|
||||
GROUPS_MANAGE_URL = 'horizon:identity:groups:manage_members'
|
||||
GROUPS_MANAGE_VIEW_TEMPLATE = 'identity/groups/manage.html'
|
||||
GROUPS_ADD_MEMBER_URL = 'horizon:identity:groups:add_members'
|
||||
GROUPS_ADD_MEMBER_VIEW_TEMPLATE = 'identity/groups/add_non_member.html'
|
||||
GROUPS_ADD_MEMBER_AJAX_VIEW_TEMPLATE = 'identity/groups/_add_non_member.html'
|
@ -17,13 +17,14 @@ from django.utils.translation import ugettext_lazy as _
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
from openstack_dashboard.dashboards.identity import dashboard
|
||||
|
||||
|
||||
class Groups(horizon.Panel):
|
||||
name = _("Groups")
|
||||
slug = 'groups'
|
||||
policy_rules = (("identity", "identity:list_groups"),)
|
||||
|
||||
|
||||
if keystone.VERSIONS.active >= 3:
|
||||
dashboard.Admin.register(Groups)
|
||||
dashboard.Identity.register(Groups)
|
@ -22,7 +22,7 @@ from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.admin.groups import constants
|
||||
from openstack_dashboard.dashboards.identity.groups import constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
@ -5,5 +5,5 @@
|
||||
{% block modal-header %}{% trans "Add Group Assignment" %}{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{% url 'horizon:admin:groups:manage_members' group.id %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:groups:manage_members' group.id %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}create_group_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:groups:create' %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:groups:create' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create Group" %}{% endblock %}
|
||||
|
||||
@ -21,5 +21,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Group" %}" />
|
||||
<a href="{% url 'horizon:admin:groups:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:groups:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_group_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:groups:update' group.id %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:groups:update' group.id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Update Group" %}{% endblock %}
|
||||
|
||||
@ -21,5 +21,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Group" %}" />
|
||||
<a href="{% url 'horizon:admin:groups:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:groups:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -3,5 +3,5 @@
|
||||
{% block title %}{% trans 'Add User to Group' %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/groups/_add_non_member.html' %}
|
||||
{% include 'identity/groups/_add_non_member.html' %}
|
||||
{% endblock %}
|
@ -7,5 +7,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/groups/_create.html' %}
|
||||
{% include 'identity/groups/_create.html' %}
|
||||
{% endblock %}
|
@ -7,5 +7,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/groups/_update.html' %}
|
||||
{% include 'identity/groups/_update.html' %}
|
||||
{% endblock %}
|
@ -21,7 +21,7 @@ from mox import IsA # noqa
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
from openstack_dashboard.dashboards.admin.groups import constants
|
||||
from openstack_dashboard.dashboards.identity.groups import constants
|
||||
|
||||
|
||||
GROUPS_INDEX_URL = reverse(constants.GROUPS_INDEX_URL)
|
@ -15,7 +15,7 @@
|
||||
from django.conf.urls import patterns # noqa
|
||||
from django.conf.urls import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.admin.groups import views
|
||||
from openstack_dashboard.dashboards.identity.groups import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
@ -18,15 +18,17 @@ 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.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from openstack_dashboard.dashboards.admin.groups import constants
|
||||
from openstack_dashboard.dashboards.admin.groups \
|
||||
from openstack_dashboard.dashboards.identity.groups import constants
|
||||
from openstack_dashboard.dashboards.identity.groups \
|
||||
import forms as project_forms
|
||||
from openstack_dashboard.dashboards.admin.groups \
|
||||
from openstack_dashboard.dashboards.identity.groups \
|
||||
import tables as project_tables
|
||||
|
||||
|
||||
@ -37,12 +39,17 @@ class IndexView(tables.DataTableView):
|
||||
def get_data(self):
|
||||
groups = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
try:
|
||||
groups = api.keystone.group_list(self.request,
|
||||
domain=domain_context)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve group list.'))
|
||||
if policy.check((("identity", "identity:list_groups"),),
|
||||
self.request):
|
||||
try:
|
||||
groups = api.keystone.group_list(self.request,
|
||||
domain=domain_context)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve group list.'))
|
||||
else:
|
||||
msg = _("Insufficient privilege level to view group information.")
|
||||
messages.info(self.request, msg)
|
||||
return groups
|
||||
|
||||
|
@ -20,12 +20,14 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
from openstack_dashboard.dashboards.identity import dashboard
|
||||
|
||||
|
||||
class Tenants(horizon.Panel):
|
||||
name = _("Projects")
|
||||
slug = 'projects'
|
||||
policy_rules = (("identity", "identity:list_projects"),
|
||||
("identity", "identity:list_user_projects"))
|
||||
|
||||
|
||||
dashboard.Admin.register(Tenants)
|
||||
dashboard.Identity.register(Tenants)
|
@ -21,13 +21,13 @@ from horizon import tables
|
||||
from keystoneclient.exceptions import Conflict # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
class ViewMembersLink(tables.LinkAction):
|
||||
name = "users"
|
||||
verbose_name = _("Modify Users")
|
||||
url = "horizon:admin:projects:update"
|
||||
url = "horizon:identity:projects:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:list_users"),
|
||||
@ -43,12 +43,13 @@ class ViewMembersLink(tables.LinkAction):
|
||||
class ViewGroupsLink(tables.LinkAction):
|
||||
name = "groups"
|
||||
verbose_name = _("Modify Groups")
|
||||
url = "horizon:admin:projects:update"
|
||||
url = "horizon:identity:projects:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:list_groups"),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return keystone.VERSIONS.active >= 3
|
||||
return api.keystone.VERSIONS.active >= 3
|
||||
|
||||
def get_link_url(self, project):
|
||||
step = 'update_group_members'
|
||||
@ -60,15 +61,18 @@ class ViewGroupsLink(tables.LinkAction):
|
||||
class UsageLink(tables.LinkAction):
|
||||
name = "usage"
|
||||
verbose_name = _("View Usage")
|
||||
url = "horizon:admin:projects:usage"
|
||||
url = "horizon:identity:projects:usage"
|
||||
icon = "stats"
|
||||
policy_rules = (("compute", "compute_extension:simple_tenant_usage:show"),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return request.user.is_superuser
|
||||
|
||||
|
||||
class CreateProject(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Project")
|
||||
url = "horizon:admin:projects:create"
|
||||
url = "horizon:identity:projects:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (('identity', 'identity:create_project'),)
|
||||
@ -80,7 +84,7 @@ class CreateProject(tables.LinkAction):
|
||||
class UpdateProject(tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Edit Project")
|
||||
url = "horizon:admin:projects:update"
|
||||
url = "horizon:identity:projects:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (('identity', 'identity:update_project'),)
|
||||
@ -92,7 +96,7 @@ class UpdateProject(tables.LinkAction):
|
||||
class ModifyQuotas(tables.LinkAction):
|
||||
name = "quotas"
|
||||
verbose_name = _("Modify Quotas")
|
||||
url = "horizon:admin:projects:update"
|
||||
url = "horizon:identity:projects:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (('compute', "compute_extension:quotas:update"),)
|
||||
@ -141,7 +145,9 @@ class UpdateRow(tables.Row):
|
||||
|
||||
class UpdateCell(tables.UpdateAction):
|
||||
def allowed(self, request, project, cell):
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
return api.keystone.keystone_can_edit_project() and \
|
||||
policy.check((("identity", "identity:update_project"),),
|
||||
request)
|
||||
|
||||
def update_cell(self, request, datum, project_id,
|
||||
cell_name, new_cell_value):
|
@ -31,7 +31,7 @@ from horizon import exceptions
|
||||
from horizon.workflows import views
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.admin.projects import workflows
|
||||
from openstack_dashboard.dashboards.identity.projects import workflows
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard import usage
|
||||
from openstack_dashboard.usage import quotas
|
||||
@ -44,22 +44,23 @@ if with_sel:
|
||||
from socket import timeout as socket_timeout # noqa
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:admin:projects:index')
|
||||
INDEX_URL = reverse('horizon:identity:projects:index')
|
||||
USER_ROLE_PREFIX = workflows.PROJECT_GROUP_MEMBER_SLUG + "_role_"
|
||||
GROUP_ROLE_PREFIX = workflows.PROJECT_USER_MEMBER_SLUG + "_role_"
|
||||
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
class TenantsViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
def test_index(self):
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
domain=None,
|
||||
paginate=True) \
|
||||
paginate=True,
|
||||
marker=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'admin/projects/index.html')
|
||||
self.assertTemplateUsed(res, 'identity/projects/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, self.tenants.list())
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_list', )})
|
||||
@ -70,16 +71,34 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
||||
domain_tenants = [tenant for tenant in self.tenants.list()
|
||||
if tenant.domain_id == domain.id]
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
domain=domain.id) \
|
||||
.AndReturn(domain_tenants)
|
||||
domain=domain.id,
|
||||
paginate=True,
|
||||
marker=None) \
|
||||
.AndReturn([domain_tenants, False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'admin/projects/index.html')
|
||||
self.assertTemplateUsed(res, 'identity/projects/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, domain_tenants)
|
||||
self.assertContains(res, "<em>test_domain:</em>")
|
||||
|
||||
|
||||
class ProjectsViewNonAdminTests(test.TestCase):
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
def test_index(self):
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
user=self.user.id,
|
||||
paginate=True,
|
||||
marker=None,
|
||||
admin=False) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'identity/projects/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, self.tenants.list())
|
||||
|
||||
|
||||
class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
def _get_project_info(self, project):
|
||||
domain = self._get_default_domain()
|
||||
@ -179,7 +198,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
@ -240,7 +259,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
.AndReturn(self.roles.list())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('horizon:admin:projects:create'))
|
||||
res = self.client.get(reverse('horizon:identity:projects:create'))
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
if django.VERSION >= (1, 6):
|
||||
@ -343,7 +362,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data.update(self._get_workflow_data(project, quota))
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
@ -406,7 +425,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
@ -462,7 +481,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data = self._get_workflow_data(project, quota)
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
@ -546,7 +565,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data.update(self._get_workflow_data(project, quota))
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
@ -631,7 +650,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data.update(self._get_workflow_data(project, quota))
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
@ -681,7 +700,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
workflow_data = self._get_workflow_data(project, quota)
|
||||
workflow_data["name"] = ""
|
||||
|
||||
url = reverse('horizon:admin:projects:create')
|
||||
url = reverse('horizon:identity:projects:create')
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
self.assertContains(res, "field is required")
|
||||
@ -796,7 +815,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.get(url)
|
||||
|
||||
@ -1028,7 +1047,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
"enabled": project.enabled}
|
||||
workflow_data.update(project_data)
|
||||
workflow_data.update(updated_quota)
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
@ -1064,7 +1083,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.get(url)
|
||||
|
||||
@ -1180,7 +1199,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
"enabled": project.enabled}
|
||||
workflow_data.update(project_data)
|
||||
workflow_data.update(updated_quota)
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
@ -1354,7 +1373,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
"enabled": project.enabled}
|
||||
workflow_data.update(project_data)
|
||||
workflow_data.update(updated_quota)
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
@ -1488,7 +1507,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
"enabled": project.enabled}
|
||||
workflow_data.update(project_data)
|
||||
workflow_data.update(updated_quota)
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
res = self.client.post(url, workflow_data)
|
||||
|
||||
@ -1520,7 +1539,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
.AndReturn(quota)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:projects:update',
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
args=[self.tenant.id])
|
||||
|
||||
try:
|
||||
@ -1586,7 +1605,7 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
project_id = self.tenants.first().id
|
||||
csv_url = reverse('horizon:admin:projects:usage',
|
||||
csv_url = reverse('horizon:identity:projects:usage',
|
||||
args=[project_id]) + "?format=csv"
|
||||
res = self.client.get(csv_url)
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.csv')
|
||||
@ -1639,7 +1658,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
|
||||
# Check the presence of the important elements
|
||||
td_element = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']")
|
||||
cell_wrapper = td_element.find_element_by_class_name(
|
||||
'table_cell_wrapper')
|
||||
@ -1656,7 +1675,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
||||
# Changing project name in cell form
|
||||
td_element = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']")
|
||||
name_input = td_element.find_element_by_tag_name('input')
|
||||
name_input.send_keys(keys.Keys.HOME)
|
||||
@ -1667,13 +1686,13 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
wait = self.ui.WebDriverWait(self.selenium, 10,
|
||||
ignored_exceptions=[socket_timeout])
|
||||
wait.until(lambda x: self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']"
|
||||
"/div[@class='table_cell_wrapper']"
|
||||
"/div[@class='table_cell_data_wrapper']"))
|
||||
# Checking new project name after cell refresh
|
||||
data_wrapper = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']"
|
||||
"/div[@class='table_cell_wrapper']"
|
||||
"/div[@class='table_cell_data_wrapper']")
|
||||
@ -1703,7 +1722,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
|
||||
# Check the presence of the important elements
|
||||
td_element = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']")
|
||||
cell_wrapper = td_element.find_element_by_class_name(
|
||||
'table_cell_wrapper')
|
||||
@ -1720,13 +1739,13 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
||||
# Click on cancel button
|
||||
td_element = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']")
|
||||
td_element.find_element_by_class_name('inline-edit-cancel').click()
|
||||
# Cancel is via javascript, so it should be immediate
|
||||
# Checking that tenant name is not changed
|
||||
data_wrapper = self.selenium.find_element_by_xpath(
|
||||
"//td[@data-update-url='/admin/projects/?action=cell_update"
|
||||
"//td[@data-update-url='/identity/?action=cell_update"
|
||||
"&table=tenants&cell_name=name&obj_id=1']"
|
||||
"/div[@class='table_cell_wrapper']"
|
||||
"/div[@class='table_cell_data_wrapper']")
|
||||
@ -1768,7 +1787,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.selenium.get("%s%s" % (self.live_server_url,
|
||||
reverse('horizon:admin:projects:create')))
|
||||
reverse('horizon:identity:projects:create')))
|
||||
|
||||
members = self.selenium.find_element_by_css_selector(member_css_class)
|
||||
|
@ -19,7 +19,7 @@
|
||||
from django.conf.urls import patterns # noqa
|
||||
from django.conf.urls import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.admin.projects import views
|
||||
from openstack_dashboard.dashboards.identity.projects import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
@ -20,18 +20,20 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.utils import memoized
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard import policy
|
||||
from openstack_dashboard import usage
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
from openstack_dashboard.dashboards.admin.projects \
|
||||
from openstack_dashboard.dashboards.identity.projects \
|
||||
import tables as project_tables
|
||||
from openstack_dashboard.dashboards.admin.projects \
|
||||
from openstack_dashboard.dashboards.identity.projects \
|
||||
import workflows as project_workflows
|
||||
from openstack_dashboard.dashboards.project.overview \
|
||||
import views as project_views
|
||||
@ -42,7 +44,7 @@ PROJECT_INFO_FIELDS = ("domain_id",
|
||||
"description",
|
||||
"enabled")
|
||||
|
||||
INDEX_URL = "horizon:admin:projects:index"
|
||||
INDEX_URL = "horizon:identity:projects:index"
|
||||
|
||||
|
||||
class TenantContextMixin(object):
|
||||
@ -64,7 +66,7 @@ class TenantContextMixin(object):
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = project_tables.TenantsTable
|
||||
template_name = 'admin/projects/index.html'
|
||||
template_name = 'identity/projects/index.html'
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
@ -74,23 +76,43 @@ class IndexView(tables.DataTableView):
|
||||
marker = self.request.GET.get(
|
||||
project_tables.TenantsTable._meta.pagination_param, None)
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
domain=domain_context,
|
||||
paginate=True,
|
||||
marker=marker)
|
||||
except Exception:
|
||||
if policy.check((("identity", "identity:list_projects"),),
|
||||
self.request):
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
domain=domain_context,
|
||||
paginate=True,
|
||||
marker=marker)
|
||||
except Exception:
|
||||
self._more = False
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project list."))
|
||||
elif policy.check((("identity", "identity:list_user_projects"),),
|
||||
self.request):
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
user=self.request.user.id,
|
||||
paginate=True,
|
||||
marker=marker,
|
||||
admin=False)
|
||||
except Exception:
|
||||
self._more = False
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project information."))
|
||||
else:
|
||||
self._more = False
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project list."))
|
||||
msg = \
|
||||
_("Insufficient privilege level to view project information.")
|
||||
messages.info(self.request, msg)
|
||||
return tenants
|
||||
|
||||
|
||||
class ProjectUsageView(usage.UsageView):
|
||||
table_class = usage.ProjectUsageTable
|
||||
usage_class = usage.ProjectUsage
|
||||
template_name = 'admin/projects/usage.html'
|
||||
template_name = 'identity/projects/usage.html'
|
||||
csv_response_class = project_views.ProjectUsageCsvRenderer
|
||||
csv_template_name = 'project/overview/usage.csv'
|
||||
|
@ -33,8 +33,8 @@ from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
INDEX_URL = "horizon:admin:projects:index"
|
||||
ADD_USER_URL = "horizon:admin:projects:create_user"
|
||||
INDEX_URL = "horizon:identity:projects:index"
|
||||
ADD_USER_URL = "horizon:identity:projects:create_user"
|
||||
PROJECT_GROUP_ENABLED = keystone.VERSIONS.active >= 3
|
||||
PROJECT_USER_MEMBER_SLUG = "update_members"
|
||||
PROJECT_GROUP_MEMBER_SLUG = "update_group_members"
|
||||
@ -340,7 +340,7 @@ class CreateProject(workflows.Workflow):
|
||||
finalize_button_name = _("Create Project")
|
||||
success_message = _('Created new project "%s".')
|
||||
failure_message = _('Unable to create project "%s".')
|
||||
success_url = "horizon:admin:projects:index"
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (CreateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectQuota)
|
||||
@ -493,7 +493,7 @@ class UpdateProject(workflows.Workflow):
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Modified project "%s".')
|
||||
failure_message = _('Unable to modify project "%s".')
|
||||
success_url = "horizon:admin:projects:index"
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (UpdateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectQuota)
|
@ -17,12 +17,14 @@ from django.utils.translation import ugettext_lazy as _
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
from openstack_dashboard.dashboards.identity import dashboard
|
||||
|
||||
|
||||
class Roles(horizon.Panel):
|
||||
name = _("Roles")
|
||||
slug = 'roles'
|
||||
policy_rules = (("identity", "identity:list_roles"),)
|
||||
|
||||
|
||||
if keystone.VERSIONS.active >= 3:
|
||||
dashboard.Admin.register(Roles)
|
||||
dashboard.Identity.register(Roles)
|
@ -22,7 +22,7 @@ from openstack_dashboard import api
|
||||
class CreateRoleLink(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Role")
|
||||
url = "horizon:admin:roles:create"
|
||||
url = "horizon:identity:roles:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("identity", "identity:create_role"),)
|
||||
@ -34,7 +34,7 @@ class CreateRoleLink(tables.LinkAction):
|
||||
class EditRoleLink(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit")
|
||||
url = "horizon:admin:roles:update"
|
||||
url = "horizon:identity:roles:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:update_role"),)
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}create_role_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:roles:create' %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:roles:create' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create Role" %}{% endblock %}
|
||||
|
||||
@ -21,5 +21,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Role" %}" />
|
||||
<a href="{% url 'horizon:admin:roles:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:roles:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_role_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:roles:update' role.id %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:roles:update' role.id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Update Role" %}{% endblock %}
|
||||
|
||||
@ -21,5 +21,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Role" %}" />
|
||||
<a href="{% url 'horizon:admin:roles:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:roles:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -8,5 +8,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/roles/_create.html' %}
|
||||
{% include 'identity/roles/_create.html' %}
|
||||
{% endblock %}
|
@ -8,5 +8,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/roles/_update.html' %}
|
||||
{% include 'identity/roles/_update.html' %}
|
||||
{% endblock %}
|
@ -22,9 +22,9 @@ from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
ROLES_INDEX_URL = reverse('horizon:admin:roles:index')
|
||||
ROLES_CREATE_URL = reverse('horizon:admin:roles:create')
|
||||
ROLES_UPDATE_URL = reverse('horizon:admin:roles:update', args=[1])
|
||||
ROLES_INDEX_URL = reverse('horizon:identity:roles:index')
|
||||
ROLES_CREATE_URL = reverse('horizon:identity:roles:create')
|
||||
ROLES_UPDATE_URL = reverse('horizon:identity:roles:update', args=[1])
|
||||
|
||||
|
||||
class RolesViewTests(test.BaseAdminViewTests):
|
||||
@ -39,7 +39,7 @@ class RolesViewTests(test.BaseAdminViewTests):
|
||||
self.assertContains(res, 'Edit')
|
||||
self.assertContains(res, 'Delete Role')
|
||||
|
||||
self.assertTemplateUsed(res, 'admin/roles/index.html')
|
||||
self.assertTemplateUsed(res, 'identity/roles/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, self.roles.list())
|
||||
|
||||
@test.create_stubs({api.keystone: ('role_list',
|
||||
@ -56,7 +56,7 @@ class RolesViewTests(test.BaseAdminViewTests):
|
||||
self.assertNotContains(res, 'Edit')
|
||||
self.assertNotContains(res, 'Delete Role')
|
||||
|
||||
self.assertTemplateUsed(res, 'admin/roles/index.html')
|
||||
self.assertTemplateUsed(res, 'identity/roles/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, self.roles.list())
|
||||
|
||||
@test.create_stubs({api.keystone: ('role_create', )})
|
@ -15,9 +15,9 @@
|
||||
from django.conf.urls import patterns # noqa
|
||||
from django.conf.urls import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.admin.roles import views
|
||||
from openstack_dashboard.dashboards.identity.roles import views
|
||||
|
||||
urlpatterns = patterns('openstack_dashboard.dashboards.admin.roles.views',
|
||||
urlpatterns = patterns('openstack_dashboard.dashboards.identity.roles.views',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<role_id>[^/]+)/update/$',
|
||||
views.UpdateView.as_view(), name='update'),
|
@ -18,42 +18,49 @@ 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.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from openstack_dashboard.dashboards.admin.roles \
|
||||
from openstack_dashboard.dashboards.identity.roles \
|
||||
import forms as project_forms
|
||||
from openstack_dashboard.dashboards.admin.roles \
|
||||
from openstack_dashboard.dashboards.identity.roles \
|
||||
import tables as project_tables
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = project_tables.RolesTable
|
||||
template_name = 'admin/roles/index.html'
|
||||
template_name = 'identity/roles/index.html'
|
||||
|
||||
def get_data(self):
|
||||
roles = []
|
||||
try:
|
||||
roles = api.keystone.role_list(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve roles list.'))
|
||||
if policy.check((("identity", "identity:list_roles"),),
|
||||
self.request):
|
||||
try:
|
||||
roles = api.keystone.role_list(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve roles list.'))
|
||||
else:
|
||||
msg = _("Insufficient privilege level to view role information.")
|
||||
messages.info(self.request, msg)
|
||||
return roles
|
||||
|
||||
|
||||
class UpdateView(forms.ModalFormView):
|
||||
form_class = project_forms.UpdateRoleForm
|
||||
template_name = 'admin/roles/update.html'
|
||||
success_url = reverse_lazy('horizon:admin:roles:index')
|
||||
template_name = 'identity/roles/update.html'
|
||||
success_url = reverse_lazy('horizon:identity:roles:index')
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
return api.keystone.role_get(self.request, self.kwargs['role_id'])
|
||||
except Exception:
|
||||
redirect = reverse("horizon:admin:roles:index")
|
||||
redirect = reverse("horizon:identity:roles:index")
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to update role.'),
|
||||
redirect=redirect)
|
||||
@ -71,5 +78,5 @@ class UpdateView(forms.ModalFormView):
|
||||
|
||||
class CreateView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateRoleForm
|
||||
template_name = 'admin/roles/create.html'
|
||||
success_url = reverse_lazy('horizon:admin:roles:index')
|
||||
template_name = 'identity/roles/create.html'
|
||||
success_url = reverse_lazy('horizon:identity:roles:index')
|
@ -67,7 +67,7 @@ class BaseUserForm(forms.SelfHandlingForm):
|
||||
return data
|
||||
|
||||
|
||||
ADD_PROJECT_URL = "horizon:admin:projects:create"
|
||||
ADD_PROJECT_URL = "horizon:identity:projects:create"
|
||||
|
||||
|
||||
class CreateUserForm(BaseUserForm):
|
@ -20,12 +20,14 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
from openstack_dashboard.dashboards.identity import dashboard
|
||||
|
||||
|
||||
class Users(horizon.Panel):
|
||||
name = _("Users")
|
||||
slug = 'users'
|
||||
policy_rules = (("identity", "identity:get_user"),
|
||||
("identity", "identity:list_users"))
|
||||
|
||||
|
||||
dashboard.Admin.register(Users)
|
||||
dashboard.Identity.register(Users)
|
@ -26,7 +26,7 @@ DISABLE = 1
|
||||
class CreateUserLink(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create User")
|
||||
url = "horizon:admin:users:create"
|
||||
url = "horizon:identity:users:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (('identity', 'identity:create_grant'),
|
||||
@ -41,7 +41,7 @@ class CreateUserLink(tables.LinkAction):
|
||||
class EditUserLink(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit")
|
||||
url = "horizon:admin:users:update"
|
||||
url = "horizon:identity:users:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:update_user"),
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}create_user_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:users:create' %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:users:create' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create User" %}{% endblock %}
|
||||
|
||||
@ -31,5 +31,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create User" %}" />
|
||||
<a href="{% url 'horizon:admin:users:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:users:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -3,7 +3,7 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_user_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:users:update' user.id %}{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:identity:users:update' user.id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Update User" %}{% endblock %}
|
||||
|
||||
@ -31,5 +31,5 @@
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update User" %}" />
|
||||
<a href="{% url 'horizon:admin:users:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
<a href="{% url 'horizon:identity:users:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -8,5 +8,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/users/_create.html' %}
|
||||
{% include 'identity/users/_create.html' %}
|
||||
{% endblock %}
|
@ -8,5 +8,5 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/users/_update.html' %}
|
||||
{% include 'identity/users/_update.html' %}
|
||||
{% endblock %}
|
@ -28,9 +28,9 @@ from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
USERS_INDEX_URL = reverse('horizon:admin:users:index')
|
||||
USER_CREATE_URL = reverse('horizon:admin:users:create')
|
||||
USER_UPDATE_URL = reverse('horizon:admin:users:update', args=[1])
|
||||
USERS_INDEX_URL = reverse('horizon:identity:users:index')
|
||||
USER_CREATE_URL = reverse('horizon:identity:users:create')
|
||||
USER_UPDATE_URL = reverse('horizon:identity:users:update', args=[1])
|
||||
|
||||
|
||||
class UsersViewTests(test.BaseAdminViewTests):
|
||||
@ -59,7 +59,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res = self.client.get(USERS_INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'admin/users/index.html')
|
||||
self.assertTemplateUsed(res, 'identity/users/index.html')
|
||||
self.assertItemsEqual(res.context['table'].data, users)
|
||||
|
||||
if domain_id:
|
@ -19,9 +19,9 @@
|
||||
from django.conf.urls import patterns # noqa
|
||||
from django.conf.urls import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.admin.users import views
|
||||
from openstack_dashboard.dashboards.identity.users import views
|
||||
|
||||
urlpatterns = patterns('openstack_dashboard.dashboards.admin.users.views',
|
||||
urlpatterns = patterns('openstack_dashboard.dashboards.identity.users.views',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<user_id>[^/]+)/update/$',
|
||||
views.UpdateView.as_view(), name='update'),
|
@ -26,37 +26,53 @@ from django.views.decorators.debug import sensitive_post_parameters # noqa
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from openstack_dashboard.dashboards.admin.users \
|
||||
from openstack_dashboard.dashboards.identity.users \
|
||||
import forms as project_forms
|
||||
from openstack_dashboard.dashboards.admin.users \
|
||||
from openstack_dashboard.dashboards.identity.users \
|
||||
import tables as project_tables
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = project_tables.UsersTable
|
||||
template_name = 'admin/users/index.html'
|
||||
template_name = 'identity/users/index.html'
|
||||
|
||||
def get_data(self):
|
||||
users = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
try:
|
||||
users = api.keystone.user_list(self.request,
|
||||
domain=domain_context)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve user list.'))
|
||||
if policy.check((("identity", "identity:list_users"),),
|
||||
self.request):
|
||||
try:
|
||||
users = api.keystone.user_list(self.request,
|
||||
domain=domain_context)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve user list.'))
|
||||
elif policy.check((("identity", "identity:get_user"),),
|
||||
self.request):
|
||||
try:
|
||||
user = api.keystone.user_get(self.request,
|
||||
self.request.user.id)
|
||||
users.append(user)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve user information.'))
|
||||
else:
|
||||
msg = _("Insufficient privilege level to view user information.")
|
||||
messages.info(self.request, msg)
|
||||
return users
|
||||
|
||||
|
||||
class UpdateView(forms.ModalFormView):
|
||||
form_class = project_forms.UpdateUserForm
|
||||
template_name = 'admin/users/update.html'
|
||||
success_url = reverse_lazy('horizon:admin:users:index')
|
||||
template_name = 'identity/users/update.html'
|
||||
success_url = reverse_lazy('horizon:identity:users:index')
|
||||
|
||||
@method_decorator(sensitive_post_parameters('password',
|
||||
'confirm_password'))
|
||||
@ -69,7 +85,7 @@ class UpdateView(forms.ModalFormView):
|
||||
return api.keystone.user_get(self.request, self.kwargs['user_id'],
|
||||
admin=True)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:admin:users:index")
|
||||
redirect = reverse("horizon:identity:users:index")
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to update user.'),
|
||||
redirect=redirect)
|
||||
@ -102,8 +118,8 @@ class UpdateView(forms.ModalFormView):
|
||||
|
||||
class CreateView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateUserForm
|
||||
template_name = 'admin/users/create.html'
|
||||
success_url = reverse_lazy('horizon:admin:users:index')
|
||||
template_name = 'identity/users/create.html'
|
||||
success_url = reverse_lazy('horizon:identity:users:index')
|
||||
|
||||
@method_decorator(sensitive_post_parameters('password',
|
||||
'confirm_password'))
|
||||
@ -115,7 +131,7 @@ class CreateView(forms.ModalFormView):
|
||||
try:
|
||||
roles = api.keystone.role_list(self.request)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:admin:users:index")
|
||||
redirect = reverse("horizon:identity:users:index")
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve user roles."),
|
||||
redirect=redirect)
|
8
openstack_dashboard/enabled/_25_identity.py
Normal file
8
openstack_dashboard/enabled/_25_identity.py
Normal file
@ -0,0 +1,8 @@
|
||||
# The name of the dashboard to be added to HORIZON['dashboards']. Required.
|
||||
DASHBOARD = 'identity'
|
||||
# If set to True, this dashboard will be set as the default dashboard.
|
||||
DEFAULT = False
|
||||
# A dictionary of exception classes to be added to HORIZON['exceptions'].
|
||||
ADD_EXCEPTIONS = {}
|
||||
# A list of applications to be added to INSTALLED_APPS.
|
||||
ADD_INSTALLED_APPS = ['openstack_dashboard.dashboards.identity']
|
@ -110,6 +110,9 @@ def check(actions, request, target={}):
|
||||
# same for user_id
|
||||
if target.get('user_id') is None:
|
||||
target['user_id'] = user.id
|
||||
# same for domain_id
|
||||
if target.get('domain_id') is None:
|
||||
target['domain_id'] = user.domain_id
|
||||
|
||||
credentials = _user_to_credentials(request, user)
|
||||
|
||||
|
@ -56,7 +56,7 @@ STATIC_URL = '/static/'
|
||||
ROOT_URLCONF = 'openstack_dashboard.urls'
|
||||
|
||||
HORIZON_CONFIG = {
|
||||
'dashboards': ('project', 'admin', 'settings', 'router',),
|
||||
'dashboards': ('project', 'admin', 'router',),
|
||||
'default_dashboard': 'project',
|
||||
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||
'ajax_queue_limit': 10,
|
||||
|
@ -45,6 +45,7 @@ INSTALLED_APPS = (
|
||||
'openstack_dashboard',
|
||||
'openstack_dashboard.dashboards.project',
|
||||
'openstack_dashboard.dashboards.admin',
|
||||
'openstack_dashboard.dashboards.identity',
|
||||
'openstack_dashboard.dashboards.settings',
|
||||
'openstack_dashboard.dashboards.router',
|
||||
)
|
||||
@ -54,7 +55,7 @@ AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
||||
SITE_BRANDING = 'OpenStack'
|
||||
|
||||
HORIZON_CONFIG = {
|
||||
'dashboards': ('project', 'admin', 'settings', 'router',),
|
||||
'dashboards': ('project', 'admin', 'identity', 'settings', 'router',),
|
||||
'default_dashboard': 'project',
|
||||
"password_validator": {
|
||||
"regex": '^.{8,18}$',
|
||||
|
Loading…
x
Reference in New Issue
Block a user