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:
@@ -61,6 +61,8 @@ class NotRegistered(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class HorizonComponent(object):
|
class HorizonComponent(object):
|
||||||
|
policy_rules = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(HorizonComponent, self).__init__()
|
super(HorizonComponent, self).__init__()
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
@@ -88,6 +90,29 @@ class HorizonComponent(object):
|
|||||||
urlpatterns = patterns('')
|
urlpatterns = patterns('')
|
||||||
return urlpatterns
|
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):
|
class Registry(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -543,6 +568,30 @@ class Dashboard(Registry, HorizonComponent):
|
|||||||
del loaders.panel_template_dirs[key]
|
del loaders.panel_template_dirs[key]
|
||||||
return success
|
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):
|
class Workflow(object):
|
||||||
def __init__(*args, **kwargs):
|
def __init__(*args, **kwargs):
|
||||||
|
@@ -3,12 +3,16 @@
|
|||||||
{% for heading, panels in components.iteritems %}
|
{% for heading, panels in components.iteritems %}
|
||||||
{% with panels|has_permissions_on_list:user as filtered_panels %}
|
{% with panels|has_permissions_on_list:user as filtered_panels %}
|
||||||
{% if 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">
|
<ul class="main_nav">
|
||||||
{% for panel in filtered_panels %}
|
{% for panel in filtered_panels %}
|
||||||
<li>
|
{% if panel in accessible_panels or current == panel.slug %}
|
||||||
<a href="{{ panel.get_absolute_url }}" {% if current == panel.slug %}class="active"{% endif %} tabindex='1'>{{ panel.name }}</a>
|
<li>
|
||||||
</li>
|
<a href="{{ panel.get_absolute_url }}" {% if current == panel.slug %}class="active"{% endif %} tabindex='1'>{{ panel.name }}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -53,15 +53,19 @@ def horizon_nav(context):
|
|||||||
for group in panel_groups.values():
|
for group in panel_groups.values():
|
||||||
allowed_panels = []
|
allowed_panels = []
|
||||||
for panel in group:
|
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)
|
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)
|
allowed_panels.append(panel)
|
||||||
if allowed_panels:
|
if allowed_panels:
|
||||||
non_empty_groups.append((group.name, 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)))
|
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)))
|
dashboards.append((dash, SortedDict(non_empty_groups)))
|
||||||
return {'components': dashboards,
|
return {'components': dashboards,
|
||||||
'user': context['request'].user,
|
'user': context['request'].user,
|
||||||
@@ -78,10 +82,11 @@ def horizon_main_nav(context):
|
|||||||
current_dashboard = context['request'].horizon.get('dashboard', None)
|
current_dashboard = context['request'].horizon.get('dashboard', None)
|
||||||
dashboards = []
|
dashboards = []
|
||||||
for dash in Horizon.get_dashboards():
|
for dash in Horizon.get_dashboards():
|
||||||
if callable(dash.nav) and dash.nav(context):
|
if dash.can_access(context['request']):
|
||||||
dashboards.append(dash)
|
if callable(dash.nav) and dash.nav(context):
|
||||||
elif dash.nav:
|
dashboards.append(dash)
|
||||||
dashboards.append(dash)
|
elif dash.nav:
|
||||||
|
dashboards.append(dash)
|
||||||
return {'components': dashboards,
|
return {'components': dashboards,
|
||||||
'user': context['request'].user,
|
'user': context['request'].user,
|
||||||
'current': current_dashboard,
|
'current': current_dashboard,
|
||||||
@@ -100,9 +105,11 @@ def horizon_dashboard_nav(context):
|
|||||||
for group in panel_groups.values():
|
for group in panel_groups.values():
|
||||||
allowed_panels = []
|
allowed_panels = []
|
||||||
for panel in group:
|
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)
|
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)
|
allowed_panels.append(panel)
|
||||||
if allowed_panels:
|
if allowed_panels:
|
||||||
non_empty_groups.append((group.name, 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'
|
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):
|
class BaseHorizonTests(test.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -439,3 +452,67 @@ class CustomPermissionsTests(BaseHorizonTests):
|
|||||||
follow=False,
|
follow=False,
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
self.assertEqual(resp.status_code, 200)
|
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)
|
return manager.delete(project)
|
||||||
|
|
||||||
|
|
||||||
def tenant_list(request, paginate=False, marker=None, domain=None, user=None):
|
def tenant_list(request, paginate=False, marker=None, domain=None, user=None,
|
||||||
manager = VERSIONS.get_project_manager(request, admin=True)
|
admin=True):
|
||||||
|
manager = VERSIONS.get_project_manager(request, admin=admin)
|
||||||
page_size = utils.get_page_size(request)
|
page_size = utils.get_page_size(request)
|
||||||
|
|
||||||
limit = None
|
limit = None
|
||||||
|
@@ -25,16 +25,10 @@ class SystemPanels(horizon.PanelGroup):
|
|||||||
'networks', 'routers', 'info')
|
'networks', 'routers', 'info')
|
||||||
|
|
||||||
|
|
||||||
class IdentityPanels(horizon.PanelGroup):
|
|
||||||
slug = "identity"
|
|
||||||
name = _("Identity")
|
|
||||||
panels = ('domains', 'projects', 'users', 'groups', 'roles')
|
|
||||||
|
|
||||||
|
|
||||||
class Admin(horizon.Dashboard):
|
class Admin(horizon.Dashboard):
|
||||||
name = _("Admin")
|
name = _("Admin")
|
||||||
slug = "admin"
|
slug = "admin"
|
||||||
panels = (SystemPanels, IdentityPanels)
|
panels = (SystemPanels,)
|
||||||
default_panel = 'overview'
|
default_panel = 'overview'
|
||||||
permissions = ('openstack.roles.admin',)
|
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",
|
DOMAIN_INFO_FIELDS = ("name",
|
||||||
"description",
|
"description",
|
||||||
"enabled")
|
"enabled")
|
||||||
DOMAINS_INDEX_URL = 'horizon:admin:domains:index'
|
DOMAINS_INDEX_URL = 'horizon:identity:domains:index'
|
||||||
DOMAINS_INDEX_VIEW_TEMPLATE = 'admin/domains/index.html'
|
DOMAINS_INDEX_VIEW_TEMPLATE = 'identity/domains/index.html'
|
||||||
DOMAINS_CREATE_URL = 'horizon:admin:domains:create'
|
DOMAINS_CREATE_URL = 'horizon:identity:domains:create'
|
||||||
DOMAINS_UPDATE_URL = 'horizon:admin:domains:update'
|
DOMAINS_UPDATE_URL = 'horizon:identity:domains:update'
|
||||||
DOMAIN_GROUP_MEMBER_SLUG = "update_group_members"
|
DOMAIN_GROUP_MEMBER_SLUG = "update_group_members"
|
@@ -17,13 +17,15 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
import horizon
|
import horizon
|
||||||
|
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
from openstack_dashboard.dashboards.admin import dashboard
|
from openstack_dashboard.dashboards.identity import dashboard
|
||||||
|
|
||||||
|
|
||||||
class Domains(horizon.Panel):
|
class Domains(horizon.Panel):
|
||||||
name = _("Domains")
|
name = _("Domains")
|
||||||
slug = 'domains'
|
slug = 'domains'
|
||||||
|
policy_rules = (("identity", "identity:get_domain"),
|
||||||
|
("identity", "identity:list_domains"))
|
||||||
|
|
||||||
|
|
||||||
if keystone.VERSIONS.active >= 3:
|
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 import api
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.domains import constants
|
from openstack_dashboard.dashboards.identity.domains import constants
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -35,7 +35,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
class ViewGroupsLink(tables.LinkAction):
|
class ViewGroupsLink(tables.LinkAction):
|
||||||
name = "groups"
|
name = "groups"
|
||||||
verbose_name = _("Modify Groups")
|
verbose_name = _("Modify Groups")
|
||||||
url = "horizon:admin:domains:update"
|
url = "horizon:identity:domains:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
|
|
@@ -24,8 +24,8 @@ from horizon.workflows import views
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.domains import constants
|
from openstack_dashboard.dashboards.identity.domains import constants
|
||||||
from openstack_dashboard.dashboards.admin.domains import workflows
|
from openstack_dashboard.dashboards.identity.domains import workflows
|
||||||
|
|
||||||
|
|
||||||
DOMAINS_INDEX_URL = reverse(constants.DOMAINS_INDEX_URL)
|
DOMAINS_INDEX_URL = reverse(constants.DOMAINS_INDEX_URL)
|
||||||
@@ -134,7 +134,7 @@ class CreateDomainWorkflowTests(test.BaseAdminViewTests):
|
|||||||
return domain_info
|
return domain_info
|
||||||
|
|
||||||
def test_add_domain_get(self):
|
def test_add_domain_get(self):
|
||||||
url = reverse('horizon:admin:domains:create')
|
url = reverse('horizon:identity:domains:create')
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
@@ -15,7 +15,7 @@
|
|||||||
from django.conf.urls import patterns # noqa
|
from django.conf.urls import patterns # noqa
|
||||||
from django.conf.urls import url # 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('',
|
urlpatterns = patterns('',
|
@@ -16,15 +16,17 @@ from django.core.urlresolvers import reverse
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon import workflows
|
from horizon import workflows
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard import policy
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.domains import constants
|
from openstack_dashboard.dashboards.identity.domains import constants
|
||||||
from openstack_dashboard.dashboards.admin.domains \
|
from openstack_dashboard.dashboards.identity.domains \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
from openstack_dashboard.dashboards.admin.domains \
|
from openstack_dashboard.dashboards.identity.domains \
|
||||||
import workflows as project_workflows
|
import workflows as project_workflows
|
||||||
|
|
||||||
|
|
||||||
@@ -35,16 +37,30 @@ class IndexView(tables.DataTableView):
|
|||||||
def get_data(self):
|
def get_data(self):
|
||||||
domains = []
|
domains = []
|
||||||
domain_context = self.request.session.get('domain_context', None)
|
domain_context = self.request.session.get('domain_context', None)
|
||||||
try:
|
if policy.check((("identity", "identity:list_domains"),),
|
||||||
if domain_context:
|
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 = api.keystone.domain_get(self.request,
|
||||||
domain_context)
|
self.request.user.domain_id)
|
||||||
domains.append(domain)
|
domains.append(domain)
|
||||||
else:
|
except Exception:
|
||||||
domains = api.keystone.domain_list(self.request)
|
exceptions.handle(self.request,
|
||||||
except Exception:
|
_('Unable to retrieve domain information.'))
|
||||||
exceptions.handle(self.request,
|
else:
|
||||||
_('Unable to retrieve domain list.'))
|
msg = _("Insufficient privilege level to view domain information.")
|
||||||
|
messages.info(self.request, msg)
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
|
|
@@ -24,7 +24,7 @@ from horizon import workflows
|
|||||||
|
|
||||||
from openstack_dashboard import api
|
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__)
|
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
|
import horizon
|
||||||
|
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
from openstack_dashboard.dashboards.admin import dashboard
|
from openstack_dashboard.dashboards.identity import dashboard
|
||||||
|
|
||||||
|
|
||||||
class Groups(horizon.Panel):
|
class Groups(horizon.Panel):
|
||||||
name = _("Groups")
|
name = _("Groups")
|
||||||
slug = 'groups'
|
slug = 'groups'
|
||||||
|
policy_rules = (("identity", "identity:list_groups"),)
|
||||||
|
|
||||||
|
|
||||||
if keystone.VERSIONS.active >= 3:
|
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 import api
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.groups import constants
|
from openstack_dashboard.dashboards.identity.groups import constants
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
@@ -5,5 +5,5 @@
|
|||||||
{% block modal-header %}{% trans "Add Group Assignment" %}{% endblock %}
|
{% block modal-header %}{% trans "Add Group Assignment" %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% 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 %}
|
{% endblock %}
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}create_group_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Create Group" %}{% endblock %}
|
||||||
|
|
||||||
@@ -21,5 +21,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Group" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}update_group_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Update Group" %}{% endblock %}
|
||||||
|
|
||||||
@@ -21,5 +21,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Group" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -3,5 +3,5 @@
|
|||||||
{% block title %}{% trans 'Add User to Group' %}{% endblock %}
|
{% block title %}{% trans 'Add User to Group' %}{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/groups/_add_non_member.html' %}
|
{% include 'identity/groups/_add_non_member.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/groups/_create.html' %}
|
{% include 'identity/groups/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/groups/_update.html' %}
|
{% include 'identity/groups/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -21,7 +21,7 @@ from mox import IsA # noqa
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.test import helpers as test
|
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)
|
GROUPS_INDEX_URL = reverse(constants.GROUPS_INDEX_URL)
|
@@ -15,7 +15,7 @@
|
|||||||
from django.conf.urls import patterns # noqa
|
from django.conf.urls import patterns # noqa
|
||||||
from django.conf.urls import url # 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('',
|
urlpatterns = patterns('',
|
@@ -18,15 +18,17 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon.utils import memoized
|
from horizon.utils import memoized
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard import policy
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.groups import constants
|
from openstack_dashboard.dashboards.identity.groups import constants
|
||||||
from openstack_dashboard.dashboards.admin.groups \
|
from openstack_dashboard.dashboards.identity.groups \
|
||||||
import forms as project_forms
|
import forms as project_forms
|
||||||
from openstack_dashboard.dashboards.admin.groups \
|
from openstack_dashboard.dashboards.identity.groups \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
|
|
||||||
|
|
||||||
@@ -37,12 +39,17 @@ class IndexView(tables.DataTableView):
|
|||||||
def get_data(self):
|
def get_data(self):
|
||||||
groups = []
|
groups = []
|
||||||
domain_context = self.request.session.get('domain_context', None)
|
domain_context = self.request.session.get('domain_context', None)
|
||||||
try:
|
if policy.check((("identity", "identity:list_groups"),),
|
||||||
groups = api.keystone.group_list(self.request,
|
self.request):
|
||||||
domain=domain_context)
|
try:
|
||||||
except Exception:
|
groups = api.keystone.group_list(self.request,
|
||||||
exceptions.handle(self.request,
|
domain=domain_context)
|
||||||
_('Unable to retrieve group list.'))
|
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
|
return groups
|
||||||
|
|
||||||
|
|
@@ -20,12 +20,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
import horizon
|
import horizon
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin import dashboard
|
from openstack_dashboard.dashboards.identity import dashboard
|
||||||
|
|
||||||
|
|
||||||
class Tenants(horizon.Panel):
|
class Tenants(horizon.Panel):
|
||||||
name = _("Projects")
|
name = _("Projects")
|
||||||
slug = '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 keystoneclient.exceptions import Conflict # noqa
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard import policy
|
||||||
|
|
||||||
|
|
||||||
class ViewMembersLink(tables.LinkAction):
|
class ViewMembersLink(tables.LinkAction):
|
||||||
name = "users"
|
name = "users"
|
||||||
verbose_name = _("Modify Users")
|
verbose_name = _("Modify Users")
|
||||||
url = "horizon:admin:projects:update"
|
url = "horizon:identity:projects:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (("identity", "identity:list_users"),
|
policy_rules = (("identity", "identity:list_users"),
|
||||||
@@ -43,12 +43,13 @@ class ViewMembersLink(tables.LinkAction):
|
|||||||
class ViewGroupsLink(tables.LinkAction):
|
class ViewGroupsLink(tables.LinkAction):
|
||||||
name = "groups"
|
name = "groups"
|
||||||
verbose_name = _("Modify Groups")
|
verbose_name = _("Modify Groups")
|
||||||
url = "horizon:admin:projects:update"
|
url = "horizon:identity:projects:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
|
policy_rules = (("identity", "identity:list_groups"),)
|
||||||
|
|
||||||
def allowed(self, request, project):
|
def allowed(self, request, project):
|
||||||
return keystone.VERSIONS.active >= 3
|
return api.keystone.VERSIONS.active >= 3
|
||||||
|
|
||||||
def get_link_url(self, project):
|
def get_link_url(self, project):
|
||||||
step = 'update_group_members'
|
step = 'update_group_members'
|
||||||
@@ -60,15 +61,18 @@ class ViewGroupsLink(tables.LinkAction):
|
|||||||
class UsageLink(tables.LinkAction):
|
class UsageLink(tables.LinkAction):
|
||||||
name = "usage"
|
name = "usage"
|
||||||
verbose_name = _("View Usage")
|
verbose_name = _("View Usage")
|
||||||
url = "horizon:admin:projects:usage"
|
url = "horizon:identity:projects:usage"
|
||||||
icon = "stats"
|
icon = "stats"
|
||||||
policy_rules = (("compute", "compute_extension:simple_tenant_usage:show"),)
|
policy_rules = (("compute", "compute_extension:simple_tenant_usage:show"),)
|
||||||
|
|
||||||
|
def allowed(self, request, project):
|
||||||
|
return request.user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
class CreateProject(tables.LinkAction):
|
class CreateProject(tables.LinkAction):
|
||||||
name = "create"
|
name = "create"
|
||||||
verbose_name = _("Create Project")
|
verbose_name = _("Create Project")
|
||||||
url = "horizon:admin:projects:create"
|
url = "horizon:identity:projects:create"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "plus"
|
icon = "plus"
|
||||||
policy_rules = (('identity', 'identity:create_project'),)
|
policy_rules = (('identity', 'identity:create_project'),)
|
||||||
@@ -80,7 +84,7 @@ class CreateProject(tables.LinkAction):
|
|||||||
class UpdateProject(tables.LinkAction):
|
class UpdateProject(tables.LinkAction):
|
||||||
name = "update"
|
name = "update"
|
||||||
verbose_name = _("Edit Project")
|
verbose_name = _("Edit Project")
|
||||||
url = "horizon:admin:projects:update"
|
url = "horizon:identity:projects:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (('identity', 'identity:update_project'),)
|
policy_rules = (('identity', 'identity:update_project'),)
|
||||||
@@ -92,7 +96,7 @@ class UpdateProject(tables.LinkAction):
|
|||||||
class ModifyQuotas(tables.LinkAction):
|
class ModifyQuotas(tables.LinkAction):
|
||||||
name = "quotas"
|
name = "quotas"
|
||||||
verbose_name = _("Modify Quotas")
|
verbose_name = _("Modify Quotas")
|
||||||
url = "horizon:admin:projects:update"
|
url = "horizon:identity:projects:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (('compute', "compute_extension:quotas:update"),)
|
policy_rules = (('compute', "compute_extension:quotas:update"),)
|
||||||
@@ -141,7 +145,9 @@ class UpdateRow(tables.Row):
|
|||||||
|
|
||||||
class UpdateCell(tables.UpdateAction):
|
class UpdateCell(tables.UpdateAction):
|
||||||
def allowed(self, request, project, cell):
|
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,
|
def update_cell(self, request, datum, project_id,
|
||||||
cell_name, new_cell_value):
|
cell_name, new_cell_value):
|
@@ -31,7 +31,7 @@ from horizon import exceptions
|
|||||||
from horizon.workflows import views
|
from horizon.workflows import views
|
||||||
|
|
||||||
from openstack_dashboard import api
|
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.test import helpers as test
|
||||||
from openstack_dashboard import usage
|
from openstack_dashboard import usage
|
||||||
from openstack_dashboard.usage import quotas
|
from openstack_dashboard.usage import quotas
|
||||||
@@ -44,22 +44,23 @@ if with_sel:
|
|||||||
from socket import timeout as socket_timeout # noqa
|
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_"
|
USER_ROLE_PREFIX = workflows.PROJECT_GROUP_MEMBER_SLUG + "_role_"
|
||||||
GROUP_ROLE_PREFIX = workflows.PROJECT_USER_MEMBER_SLUG + "_role_"
|
GROUP_ROLE_PREFIX = workflows.PROJECT_USER_MEMBER_SLUG + "_role_"
|
||||||
|
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
|
||||||
class TenantsViewTests(test.BaseAdminViewTests):
|
class TenantsViewTests(test.BaseAdminViewTests):
|
||||||
|
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||||
domain=None,
|
domain=None,
|
||||||
paginate=True) \
|
paginate=True,
|
||||||
|
marker=None) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
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())
|
self.assertItemsEqual(res.context['table'].data, self.tenants.list())
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_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()
|
domain_tenants = [tenant for tenant in self.tenants.list()
|
||||||
if tenant.domain_id == domain.id]
|
if tenant.domain_id == domain.id]
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id) \
|
domain=domain.id,
|
||||||
.AndReturn(domain_tenants)
|
paginate=True,
|
||||||
|
marker=None) \
|
||||||
|
.AndReturn([domain_tenants, False])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
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.assertItemsEqual(res.context['table'].data, domain_tenants)
|
||||||
self.assertContains(res, "<em>test_domain:</em>")
|
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):
|
class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||||
def _get_project_info(self, project):
|
def _get_project_info(self, project):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
@@ -179,7 +198,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:create')
|
url = reverse('horizon:identity:projects:create')
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||||
@@ -240,7 +259,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(self.roles.list())
|
.AndReturn(self.roles.list())
|
||||||
self.mox.ReplayAll()
|
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)
|
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||||
if django.VERSION >= (1, 6):
|
if django.VERSION >= (1, 6):
|
||||||
@@ -343,7 +362,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data.update(self._get_workflow_data(project, quota))
|
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)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
@@ -406,7 +425,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:create')
|
url = reverse('horizon:identity:projects:create')
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||||
@@ -462,7 +481,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data = self._get_workflow_data(project, quota)
|
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)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
@@ -546,7 +565,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data.update(self._get_workflow_data(project, quota))
|
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)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
@@ -631,7 +650,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data.update(self._get_workflow_data(project, quota))
|
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)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
@@ -681,7 +700,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
workflow_data = self._get_workflow_data(project, quota)
|
workflow_data = self._get_workflow_data(project, quota)
|
||||||
workflow_data["name"] = ""
|
workflow_data["name"] = ""
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:create')
|
url = reverse('horizon:identity:projects:create')
|
||||||
res = self.client.post(url, workflow_data)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
self.assertContains(res, "field is required")
|
self.assertContains(res, "field is required")
|
||||||
@@ -796,7 +815,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
@@ -1028,7 +1047,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
"enabled": project.enabled}
|
"enabled": project.enabled}
|
||||||
workflow_data.update(project_data)
|
workflow_data.update(project_data)
|
||||||
workflow_data.update(updated_quota)
|
workflow_data.update(updated_quota)
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.post(url, workflow_data)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
@@ -1064,7 +1083,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
@@ -1180,7 +1199,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
"enabled": project.enabled}
|
"enabled": project.enabled}
|
||||||
workflow_data.update(project_data)
|
workflow_data.update(project_data)
|
||||||
workflow_data.update(updated_quota)
|
workflow_data.update(updated_quota)
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.post(url, workflow_data)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
@@ -1354,7 +1373,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
"enabled": project.enabled}
|
"enabled": project.enabled}
|
||||||
workflow_data.update(project_data)
|
workflow_data.update(project_data)
|
||||||
workflow_data.update(updated_quota)
|
workflow_data.update(updated_quota)
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.post(url, workflow_data)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
@@ -1488,7 +1507,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
"enabled": project.enabled}
|
"enabled": project.enabled}
|
||||||
workflow_data.update(project_data)
|
workflow_data.update(project_data)
|
||||||
workflow_data.update(updated_quota)
|
workflow_data.update(updated_quota)
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
res = self.client.post(url, workflow_data)
|
res = self.client.post(url, workflow_data)
|
||||||
|
|
||||||
@@ -1520,7 +1539,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(quota)
|
.AndReturn(quota)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:admin:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
args=[self.tenant.id])
|
args=[self.tenant.id])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1586,7 +1605,7 @@ class UsageViewTests(test.BaseAdminViewTests):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
project_id = self.tenants.first().id
|
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"
|
args=[project_id]) + "?format=csv"
|
||||||
res = self.client.get(csv_url)
|
res = self.client.get(csv_url)
|
||||||
self.assertTemplateUsed(res, 'project/overview/usage.csv')
|
self.assertTemplateUsed(res, 'project/overview/usage.csv')
|
||||||
@@ -1639,7 +1658,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
|
|
||||||
# Check the presence of the important elements
|
# Check the presence of the important elements
|
||||||
td_element = self.selenium.find_element_by_xpath(
|
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']")
|
"&table=tenants&cell_name=name&obj_id=1']")
|
||||||
cell_wrapper = td_element.find_element_by_class_name(
|
cell_wrapper = td_element.find_element_by_class_name(
|
||||||
'table_cell_wrapper')
|
'table_cell_wrapper')
|
||||||
@@ -1656,7 +1675,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
||||||
# Changing project name in cell form
|
# Changing project name in cell form
|
||||||
td_element = self.selenium.find_element_by_xpath(
|
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']")
|
"&table=tenants&cell_name=name&obj_id=1']")
|
||||||
name_input = td_element.find_element_by_tag_name('input')
|
name_input = td_element.find_element_by_tag_name('input')
|
||||||
name_input.send_keys(keys.Keys.HOME)
|
name_input.send_keys(keys.Keys.HOME)
|
||||||
@@ -1667,13 +1686,13 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
wait = self.ui.WebDriverWait(self.selenium, 10,
|
wait = self.ui.WebDriverWait(self.selenium, 10,
|
||||||
ignored_exceptions=[socket_timeout])
|
ignored_exceptions=[socket_timeout])
|
||||||
wait.until(lambda x: self.selenium.find_element_by_xpath(
|
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']"
|
"&table=tenants&cell_name=name&obj_id=1']"
|
||||||
"/div[@class='table_cell_wrapper']"
|
"/div[@class='table_cell_wrapper']"
|
||||||
"/div[@class='table_cell_data_wrapper']"))
|
"/div[@class='table_cell_data_wrapper']"))
|
||||||
# Checking new project name after cell refresh
|
# Checking new project name after cell refresh
|
||||||
data_wrapper = self.selenium.find_element_by_xpath(
|
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']"
|
"&table=tenants&cell_name=name&obj_id=1']"
|
||||||
"/div[@class='table_cell_wrapper']"
|
"/div[@class='table_cell_wrapper']"
|
||||||
"/div[@class='table_cell_data_wrapper']")
|
"/div[@class='table_cell_data_wrapper']")
|
||||||
@@ -1703,7 +1722,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
|
|
||||||
# Check the presence of the important elements
|
# Check the presence of the important elements
|
||||||
td_element = self.selenium.find_element_by_xpath(
|
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']")
|
"&table=tenants&cell_name=name&obj_id=1']")
|
||||||
cell_wrapper = td_element.find_element_by_class_name(
|
cell_wrapper = td_element.find_element_by_class_name(
|
||||||
'table_cell_wrapper')
|
'table_cell_wrapper')
|
||||||
@@ -1720,13 +1739,13 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
wait.until(lambda x: self.selenium.find_element_by_name("name__1"))
|
||||||
# Click on cancel button
|
# Click on cancel button
|
||||||
td_element = self.selenium.find_element_by_xpath(
|
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']")
|
"&table=tenants&cell_name=name&obj_id=1']")
|
||||||
td_element.find_element_by_class_name('inline-edit-cancel').click()
|
td_element.find_element_by_class_name('inline-edit-cancel').click()
|
||||||
# Cancel is via javascript, so it should be immediate
|
# Cancel is via javascript, so it should be immediate
|
||||||
# Checking that tenant name is not changed
|
# Checking that tenant name is not changed
|
||||||
data_wrapper = self.selenium.find_element_by_xpath(
|
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']"
|
"&table=tenants&cell_name=name&obj_id=1']"
|
||||||
"/div[@class='table_cell_wrapper']"
|
"/div[@class='table_cell_wrapper']"
|
||||||
"/div[@class='table_cell_data_wrapper']")
|
"/div[@class='table_cell_data_wrapper']")
|
||||||
@@ -1768,7 +1787,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
self.selenium.get("%s%s" % (self.live_server_url,
|
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)
|
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 patterns # noqa
|
||||||
from django.conf.urls import url # 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('',
|
urlpatterns = patterns('',
|
@@ -20,18 +20,20 @@ from django.core.urlresolvers import reverse
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon.utils import memoized
|
from horizon.utils import memoized
|
||||||
from horizon import workflows
|
from horizon import workflows
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
|
from openstack_dashboard import policy
|
||||||
from openstack_dashboard import usage
|
from openstack_dashboard import usage
|
||||||
from openstack_dashboard.usage import quotas
|
from openstack_dashboard.usage import quotas
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin.projects \
|
from openstack_dashboard.dashboards.identity.projects \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
from openstack_dashboard.dashboards.admin.projects \
|
from openstack_dashboard.dashboards.identity.projects \
|
||||||
import workflows as project_workflows
|
import workflows as project_workflows
|
||||||
from openstack_dashboard.dashboards.project.overview \
|
from openstack_dashboard.dashboards.project.overview \
|
||||||
import views as project_views
|
import views as project_views
|
||||||
@@ -42,7 +44,7 @@ PROJECT_INFO_FIELDS = ("domain_id",
|
|||||||
"description",
|
"description",
|
||||||
"enabled")
|
"enabled")
|
||||||
|
|
||||||
INDEX_URL = "horizon:admin:projects:index"
|
INDEX_URL = "horizon:identity:projects:index"
|
||||||
|
|
||||||
|
|
||||||
class TenantContextMixin(object):
|
class TenantContextMixin(object):
|
||||||
@@ -64,7 +66,7 @@ class TenantContextMixin(object):
|
|||||||
|
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = project_tables.TenantsTable
|
table_class = project_tables.TenantsTable
|
||||||
template_name = 'admin/projects/index.html'
|
template_name = 'identity/projects/index.html'
|
||||||
|
|
||||||
def has_more_data(self, table):
|
def has_more_data(self, table):
|
||||||
return self._more
|
return self._more
|
||||||
@@ -74,23 +76,43 @@ class IndexView(tables.DataTableView):
|
|||||||
marker = self.request.GET.get(
|
marker = self.request.GET.get(
|
||||||
project_tables.TenantsTable._meta.pagination_param, None)
|
project_tables.TenantsTable._meta.pagination_param, None)
|
||||||
domain_context = self.request.session.get('domain_context', None)
|
domain_context = self.request.session.get('domain_context', None)
|
||||||
try:
|
if policy.check((("identity", "identity:list_projects"),),
|
||||||
tenants, self._more = api.keystone.tenant_list(
|
self.request):
|
||||||
self.request,
|
try:
|
||||||
domain=domain_context,
|
tenants, self._more = api.keystone.tenant_list(
|
||||||
paginate=True,
|
self.request,
|
||||||
marker=marker)
|
domain=domain_context,
|
||||||
except Exception:
|
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
|
self._more = False
|
||||||
exceptions.handle(self.request,
|
msg = \
|
||||||
_("Unable to retrieve project list."))
|
_("Insufficient privilege level to view project information.")
|
||||||
|
messages.info(self.request, msg)
|
||||||
return tenants
|
return tenants
|
||||||
|
|
||||||
|
|
||||||
class ProjectUsageView(usage.UsageView):
|
class ProjectUsageView(usage.UsageView):
|
||||||
table_class = usage.ProjectUsageTable
|
table_class = usage.ProjectUsageTable
|
||||||
usage_class = usage.ProjectUsage
|
usage_class = usage.ProjectUsage
|
||||||
template_name = 'admin/projects/usage.html'
|
template_name = 'identity/projects/usage.html'
|
||||||
csv_response_class = project_views.ProjectUsageCsvRenderer
|
csv_response_class = project_views.ProjectUsageCsvRenderer
|
||||||
csv_template_name = 'project/overview/usage.csv'
|
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.api import nova
|
||||||
from openstack_dashboard.usage import quotas
|
from openstack_dashboard.usage import quotas
|
||||||
|
|
||||||
INDEX_URL = "horizon:admin:projects:index"
|
INDEX_URL = "horizon:identity:projects:index"
|
||||||
ADD_USER_URL = "horizon:admin:projects:create_user"
|
ADD_USER_URL = "horizon:identity:projects:create_user"
|
||||||
PROJECT_GROUP_ENABLED = keystone.VERSIONS.active >= 3
|
PROJECT_GROUP_ENABLED = keystone.VERSIONS.active >= 3
|
||||||
PROJECT_USER_MEMBER_SLUG = "update_members"
|
PROJECT_USER_MEMBER_SLUG = "update_members"
|
||||||
PROJECT_GROUP_MEMBER_SLUG = "update_group_members"
|
PROJECT_GROUP_MEMBER_SLUG = "update_group_members"
|
||||||
@@ -340,7 +340,7 @@ class CreateProject(workflows.Workflow):
|
|||||||
finalize_button_name = _("Create Project")
|
finalize_button_name = _("Create Project")
|
||||||
success_message = _('Created new project "%s".')
|
success_message = _('Created new project "%s".')
|
||||||
failure_message = _('Unable to create project "%s".')
|
failure_message = _('Unable to create project "%s".')
|
||||||
success_url = "horizon:admin:projects:index"
|
success_url = "horizon:identity:projects:index"
|
||||||
default_steps = (CreateProjectInfo,
|
default_steps = (CreateProjectInfo,
|
||||||
UpdateProjectMembers,
|
UpdateProjectMembers,
|
||||||
UpdateProjectQuota)
|
UpdateProjectQuota)
|
||||||
@@ -493,7 +493,7 @@ class UpdateProject(workflows.Workflow):
|
|||||||
finalize_button_name = _("Save")
|
finalize_button_name = _("Save")
|
||||||
success_message = _('Modified project "%s".')
|
success_message = _('Modified project "%s".')
|
||||||
failure_message = _('Unable to modify project "%s".')
|
failure_message = _('Unable to modify project "%s".')
|
||||||
success_url = "horizon:admin:projects:index"
|
success_url = "horizon:identity:projects:index"
|
||||||
default_steps = (UpdateProjectInfo,
|
default_steps = (UpdateProjectInfo,
|
||||||
UpdateProjectMembers,
|
UpdateProjectMembers,
|
||||||
UpdateProjectQuota)
|
UpdateProjectQuota)
|
@@ -17,12 +17,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
import horizon
|
import horizon
|
||||||
|
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
from openstack_dashboard.dashboards.admin import dashboard
|
from openstack_dashboard.dashboards.identity import dashboard
|
||||||
|
|
||||||
|
|
||||||
class Roles(horizon.Panel):
|
class Roles(horizon.Panel):
|
||||||
name = _("Roles")
|
name = _("Roles")
|
||||||
slug = 'roles'
|
slug = 'roles'
|
||||||
|
policy_rules = (("identity", "identity:list_roles"),)
|
||||||
|
|
||||||
|
|
||||||
if keystone.VERSIONS.active >= 3:
|
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):
|
class CreateRoleLink(tables.LinkAction):
|
||||||
name = "create"
|
name = "create"
|
||||||
verbose_name = _("Create Role")
|
verbose_name = _("Create Role")
|
||||||
url = "horizon:admin:roles:create"
|
url = "horizon:identity:roles:create"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "plus"
|
icon = "plus"
|
||||||
policy_rules = (("identity", "identity:create_role"),)
|
policy_rules = (("identity", "identity:create_role"),)
|
||||||
@@ -34,7 +34,7 @@ class CreateRoleLink(tables.LinkAction):
|
|||||||
class EditRoleLink(tables.LinkAction):
|
class EditRoleLink(tables.LinkAction):
|
||||||
name = "edit"
|
name = "edit"
|
||||||
verbose_name = _("Edit")
|
verbose_name = _("Edit")
|
||||||
url = "horizon:admin:roles:update"
|
url = "horizon:identity:roles:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (("identity", "identity:update_role"),)
|
policy_rules = (("identity", "identity:update_role"),)
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}create_role_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Create Role" %}{% endblock %}
|
||||||
|
|
||||||
@@ -21,5 +21,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Role" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}update_role_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Update Role" %}{% endblock %}
|
||||||
|
|
||||||
@@ -21,5 +21,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Role" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -8,5 +8,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/roles/_create.html' %}
|
{% include 'identity/roles/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -8,5 +8,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/roles/_update.html' %}
|
{% include 'identity/roles/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -22,9 +22,9 @@ from openstack_dashboard import api
|
|||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
ROLES_INDEX_URL = reverse('horizon:admin:roles:index')
|
ROLES_INDEX_URL = reverse('horizon:identity:roles:index')
|
||||||
ROLES_CREATE_URL = reverse('horizon:admin:roles:create')
|
ROLES_CREATE_URL = reverse('horizon:identity:roles:create')
|
||||||
ROLES_UPDATE_URL = reverse('horizon:admin:roles:update', args=[1])
|
ROLES_UPDATE_URL = reverse('horizon:identity:roles:update', args=[1])
|
||||||
|
|
||||||
|
|
||||||
class RolesViewTests(test.BaseAdminViewTests):
|
class RolesViewTests(test.BaseAdminViewTests):
|
||||||
@@ -39,7 +39,7 @@ class RolesViewTests(test.BaseAdminViewTests):
|
|||||||
self.assertContains(res, 'Edit')
|
self.assertContains(res, 'Edit')
|
||||||
self.assertContains(res, 'Delete Role')
|
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())
|
self.assertItemsEqual(res.context['table'].data, self.roles.list())
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('role_list',
|
@test.create_stubs({api.keystone: ('role_list',
|
||||||
@@ -56,7 +56,7 @@ class RolesViewTests(test.BaseAdminViewTests):
|
|||||||
self.assertNotContains(res, 'Edit')
|
self.assertNotContains(res, 'Edit')
|
||||||
self.assertNotContains(res, 'Delete Role')
|
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())
|
self.assertItemsEqual(res.context['table'].data, self.roles.list())
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('role_create', )})
|
@test.create_stubs({api.keystone: ('role_create', )})
|
@@ -15,9 +15,9 @@
|
|||||||
from django.conf.urls import patterns # noqa
|
from django.conf.urls import patterns # noqa
|
||||||
from django.conf.urls import url # 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'^$', views.IndexView.as_view(), name='index'),
|
||||||
url(r'^(?P<role_id>[^/]+)/update/$',
|
url(r'^(?P<role_id>[^/]+)/update/$',
|
||||||
views.UpdateView.as_view(), name='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 exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon.utils import memoized
|
from horizon.utils import memoized
|
||||||
|
|
||||||
from openstack_dashboard import api
|
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
|
import forms as project_forms
|
||||||
from openstack_dashboard.dashboards.admin.roles \
|
from openstack_dashboard.dashboards.identity.roles \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
|
|
||||||
|
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = project_tables.RolesTable
|
table_class = project_tables.RolesTable
|
||||||
template_name = 'admin/roles/index.html'
|
template_name = 'identity/roles/index.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
roles = []
|
roles = []
|
||||||
try:
|
if policy.check((("identity", "identity:list_roles"),),
|
||||||
roles = api.keystone.role_list(self.request)
|
self.request):
|
||||||
except Exception:
|
try:
|
||||||
exceptions.handle(self.request,
|
roles = api.keystone.role_list(self.request)
|
||||||
_('Unable to retrieve roles list.'))
|
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
|
return roles
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = project_forms.UpdateRoleForm
|
form_class = project_forms.UpdateRoleForm
|
||||||
template_name = 'admin/roles/update.html'
|
template_name = 'identity/roles/update.html'
|
||||||
success_url = reverse_lazy('horizon:admin:roles:index')
|
success_url = reverse_lazy('horizon:identity:roles:index')
|
||||||
|
|
||||||
@memoized.memoized_method
|
@memoized.memoized_method
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
try:
|
try:
|
||||||
return api.keystone.role_get(self.request, self.kwargs['role_id'])
|
return api.keystone.role_get(self.request, self.kwargs['role_id'])
|
||||||
except Exception:
|
except Exception:
|
||||||
redirect = reverse("horizon:admin:roles:index")
|
redirect = reverse("horizon:identity:roles:index")
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to update role.'),
|
_('Unable to update role.'),
|
||||||
redirect=redirect)
|
redirect=redirect)
|
||||||
@@ -71,5 +78,5 @@ class UpdateView(forms.ModalFormView):
|
|||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = project_forms.CreateRoleForm
|
form_class = project_forms.CreateRoleForm
|
||||||
template_name = 'admin/roles/create.html'
|
template_name = 'identity/roles/create.html'
|
||||||
success_url = reverse_lazy('horizon:admin:roles:index')
|
success_url = reverse_lazy('horizon:identity:roles:index')
|
@@ -67,7 +67,7 @@ class BaseUserForm(forms.SelfHandlingForm):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
ADD_PROJECT_URL = "horizon:admin:projects:create"
|
ADD_PROJECT_URL = "horizon:identity:projects:create"
|
||||||
|
|
||||||
|
|
||||||
class CreateUserForm(BaseUserForm):
|
class CreateUserForm(BaseUserForm):
|
@@ -20,12 +20,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
import horizon
|
import horizon
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.admin import dashboard
|
from openstack_dashboard.dashboards.identity import dashboard
|
||||||
|
|
||||||
|
|
||||||
class Users(horizon.Panel):
|
class Users(horizon.Panel):
|
||||||
name = _("Users")
|
name = _("Users")
|
||||||
slug = '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):
|
class CreateUserLink(tables.LinkAction):
|
||||||
name = "create"
|
name = "create"
|
||||||
verbose_name = _("Create User")
|
verbose_name = _("Create User")
|
||||||
url = "horizon:admin:users:create"
|
url = "horizon:identity:users:create"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "plus"
|
icon = "plus"
|
||||||
policy_rules = (('identity', 'identity:create_grant'),
|
policy_rules = (('identity', 'identity:create_grant'),
|
||||||
@@ -41,7 +41,7 @@ class CreateUserLink(tables.LinkAction):
|
|||||||
class EditUserLink(tables.LinkAction):
|
class EditUserLink(tables.LinkAction):
|
||||||
name = "edit"
|
name = "edit"
|
||||||
verbose_name = _("Edit")
|
verbose_name = _("Edit")
|
||||||
url = "horizon:admin:users:update"
|
url = "horizon:identity:users:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (("identity", "identity:update_user"),
|
policy_rules = (("identity", "identity:update_user"),
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}create_user_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Create User" %}{% endblock %}
|
||||||
|
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create User" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -3,7 +3,7 @@
|
|||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
|
||||||
{% block form_id %}update_user_form{% endblock %}
|
{% 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 %}
|
{% block modal-header %}{% trans "Update User" %}{% endblock %}
|
||||||
|
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update User" %}" />
|
<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 %}
|
{% endblock %}
|
@@ -8,5 +8,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/users/_create.html' %}
|
{% include 'identity/users/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -8,5 +8,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/users/_update.html' %}
|
{% include 'identity/users/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -28,9 +28,9 @@ from openstack_dashboard import api
|
|||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
USERS_INDEX_URL = reverse('horizon:admin:users:index')
|
USERS_INDEX_URL = reverse('horizon:identity:users:index')
|
||||||
USER_CREATE_URL = reverse('horizon:admin:users:create')
|
USER_CREATE_URL = reverse('horizon:identity:users:create')
|
||||||
USER_UPDATE_URL = reverse('horizon:admin:users:update', args=[1])
|
USER_UPDATE_URL = reverse('horizon:identity:users:update', args=[1])
|
||||||
|
|
||||||
|
|
||||||
class UsersViewTests(test.BaseAdminViewTests):
|
class UsersViewTests(test.BaseAdminViewTests):
|
||||||
@@ -59,7 +59,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(USERS_INDEX_URL)
|
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)
|
self.assertItemsEqual(res.context['table'].data, users)
|
||||||
|
|
||||||
if domain_id:
|
if domain_id:
|
@@ -19,9 +19,9 @@
|
|||||||
from django.conf.urls import patterns # noqa
|
from django.conf.urls import patterns # noqa
|
||||||
from django.conf.urls import url # 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'^$', views.IndexView.as_view(), name='index'),
|
||||||
url(r'^(?P<user_id>[^/]+)/update/$',
|
url(r'^(?P<user_id>[^/]+)/update/$',
|
||||||
views.UpdateView.as_view(), name='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 exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon.utils import memoized
|
from horizon.utils import memoized
|
||||||
|
|
||||||
from openstack_dashboard import api
|
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
|
import forms as project_forms
|
||||||
from openstack_dashboard.dashboards.admin.users \
|
from openstack_dashboard.dashboards.identity.users \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
|
|
||||||
|
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = project_tables.UsersTable
|
table_class = project_tables.UsersTable
|
||||||
template_name = 'admin/users/index.html'
|
template_name = 'identity/users/index.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
users = []
|
users = []
|
||||||
domain_context = self.request.session.get('domain_context', None)
|
domain_context = self.request.session.get('domain_context', None)
|
||||||
try:
|
if policy.check((("identity", "identity:list_users"),),
|
||||||
users = api.keystone.user_list(self.request,
|
self.request):
|
||||||
domain=domain_context)
|
try:
|
||||||
except Exception:
|
users = api.keystone.user_list(self.request,
|
||||||
exceptions.handle(self.request,
|
domain=domain_context)
|
||||||
_('Unable to retrieve user list.'))
|
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
|
return users
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = project_forms.UpdateUserForm
|
form_class = project_forms.UpdateUserForm
|
||||||
template_name = 'admin/users/update.html'
|
template_name = 'identity/users/update.html'
|
||||||
success_url = reverse_lazy('horizon:admin:users:index')
|
success_url = reverse_lazy('horizon:identity:users:index')
|
||||||
|
|
||||||
@method_decorator(sensitive_post_parameters('password',
|
@method_decorator(sensitive_post_parameters('password',
|
||||||
'confirm_password'))
|
'confirm_password'))
|
||||||
@@ -69,7 +85,7 @@ class UpdateView(forms.ModalFormView):
|
|||||||
return api.keystone.user_get(self.request, self.kwargs['user_id'],
|
return api.keystone.user_get(self.request, self.kwargs['user_id'],
|
||||||
admin=True)
|
admin=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
redirect = reverse("horizon:admin:users:index")
|
redirect = reverse("horizon:identity:users:index")
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to update user.'),
|
_('Unable to update user.'),
|
||||||
redirect=redirect)
|
redirect=redirect)
|
||||||
@@ -102,8 +118,8 @@ class UpdateView(forms.ModalFormView):
|
|||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = project_forms.CreateUserForm
|
form_class = project_forms.CreateUserForm
|
||||||
template_name = 'admin/users/create.html'
|
template_name = 'identity/users/create.html'
|
||||||
success_url = reverse_lazy('horizon:admin:users:index')
|
success_url = reverse_lazy('horizon:identity:users:index')
|
||||||
|
|
||||||
@method_decorator(sensitive_post_parameters('password',
|
@method_decorator(sensitive_post_parameters('password',
|
||||||
'confirm_password'))
|
'confirm_password'))
|
||||||
@@ -115,7 +131,7 @@ class CreateView(forms.ModalFormView):
|
|||||||
try:
|
try:
|
||||||
roles = api.keystone.role_list(self.request)
|
roles = api.keystone.role_list(self.request)
|
||||||
except Exception:
|
except Exception:
|
||||||
redirect = reverse("horizon:admin:users:index")
|
redirect = reverse("horizon:identity:users:index")
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_("Unable to retrieve user roles."),
|
_("Unable to retrieve user roles."),
|
||||||
redirect=redirect)
|
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
|
# same for user_id
|
||||||
if target.get('user_id') is None:
|
if target.get('user_id') is None:
|
||||||
target['user_id'] = user.id
|
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)
|
credentials = _user_to_credentials(request, user)
|
||||||
|
|
||||||
|
@@ -56,7 +56,7 @@ STATIC_URL = '/static/'
|
|||||||
ROOT_URLCONF = 'openstack_dashboard.urls'
|
ROOT_URLCONF = 'openstack_dashboard.urls'
|
||||||
|
|
||||||
HORIZON_CONFIG = {
|
HORIZON_CONFIG = {
|
||||||
'dashboards': ('project', 'admin', 'settings', 'router',),
|
'dashboards': ('project', 'admin', 'router',),
|
||||||
'default_dashboard': 'project',
|
'default_dashboard': 'project',
|
||||||
'user_home': 'openstack_dashboard.views.get_user_home',
|
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||||
'ajax_queue_limit': 10,
|
'ajax_queue_limit': 10,
|
||||||
|
@@ -45,6 +45,7 @@ INSTALLED_APPS = (
|
|||||||
'openstack_dashboard',
|
'openstack_dashboard',
|
||||||
'openstack_dashboard.dashboards.project',
|
'openstack_dashboard.dashboards.project',
|
||||||
'openstack_dashboard.dashboards.admin',
|
'openstack_dashboard.dashboards.admin',
|
||||||
|
'openstack_dashboard.dashboards.identity',
|
||||||
'openstack_dashboard.dashboards.settings',
|
'openstack_dashboard.dashboards.settings',
|
||||||
'openstack_dashboard.dashboards.router',
|
'openstack_dashboard.dashboards.router',
|
||||||
)
|
)
|
||||||
@@ -54,7 +55,7 @@ AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
|||||||
SITE_BRANDING = 'OpenStack'
|
SITE_BRANDING = 'OpenStack'
|
||||||
|
|
||||||
HORIZON_CONFIG = {
|
HORIZON_CONFIG = {
|
||||||
'dashboards': ('project', 'admin', 'settings', 'router',),
|
'dashboards': ('project', 'admin', 'identity', 'settings', 'router',),
|
||||||
'default_dashboard': 'project',
|
'default_dashboard': 'project',
|
||||||
"password_validator": {
|
"password_validator": {
|
||||||
"regex": '^.{8,18}$',
|
"regex": '^.{8,18}$',
|
||||||
|
Reference in New Issue
Block a user