Merge "Retrieve domain scoped token"
This commit is contained in:
commit
711f88dd47
@ -40,6 +40,8 @@ from openstack_dashboard import policy
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEFAULT_ROLE = None
|
||||
DEFAULT_DOMAIN = getattr(settings, 'OPENSTACK_KEYSTONE_DEFAULT_DOMAIN',
|
||||
'default')
|
||||
|
||||
|
||||
# Set up our data structure for managing Identity API versions, and
|
||||
@ -144,7 +146,17 @@ def keystoneclient(request, admin=False):
|
||||
The client is cached so that subsequent API calls during the same
|
||||
request/response cycle don't have to be re-authenticated.
|
||||
"""
|
||||
api_version = VERSIONS.get_active_version()
|
||||
user = request.user
|
||||
token_id = user.token.id
|
||||
|
||||
if is_multi_domain_enabled:
|
||||
# Cloud Admin, Domain Admin or Mixed Domain Admin
|
||||
if is_domain_admin(request):
|
||||
domain_token = request.session.get('domain_token')
|
||||
if domain_token:
|
||||
token_id = getattr(domain_token, 'auth_token', None)
|
||||
|
||||
if admin:
|
||||
if not policy.check((("identity", "admin_required"),), request):
|
||||
raise exceptions.NotAuthorized
|
||||
@ -154,8 +166,6 @@ def keystoneclient(request, admin=False):
|
||||
'OPENSTACK_ENDPOINT_TYPE',
|
||||
'internalURL')
|
||||
|
||||
api_version = VERSIONS.get_active_version()
|
||||
|
||||
# Take care of client connection caching/fetching a new client.
|
||||
# Admin vs. non-admin clients are cached separately for token matching.
|
||||
cache_attr = "_keystoneclient_admin" if admin \
|
||||
@ -170,7 +180,7 @@ def keystoneclient(request, admin=False):
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
LOG.debug("Creating a new keystoneclient connection to %s." % endpoint)
|
||||
remote_addr = request.environ.get('REMOTE_ADDR', '')
|
||||
conn = api_version['client'].Client(token=user.token.id,
|
||||
conn = api_version['client'].Client(token=token_id,
|
||||
endpoint=endpoint,
|
||||
original_ip=remote_addr,
|
||||
insecure=insecure,
|
||||
@ -183,7 +193,7 @@ def keystoneclient(request, admin=False):
|
||||
|
||||
def domain_create(request, name, description=None, enabled=None):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.create(name,
|
||||
return manager.create(name=name,
|
||||
description=description,
|
||||
enabled=enabled)
|
||||
|
||||
@ -203,10 +213,29 @@ def domain_list(request):
|
||||
return manager.list()
|
||||
|
||||
|
||||
def domain_lookup(request):
|
||||
if policy.check((("identity", "identity:list_domains"),), request):
|
||||
try:
|
||||
domains = domain_list(request)
|
||||
return dict((d.id, d.name) for d in domains)
|
||||
except Exception:
|
||||
LOG.warn("Pure project admin doesn't have a domain token")
|
||||
return None
|
||||
else:
|
||||
domain = get_default_domain(request)
|
||||
return {domain.id: domain.name}
|
||||
|
||||
|
||||
def domain_update(request, domain_id, name=None, description=None,
|
||||
enabled=None):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.update(domain_id, name, description, enabled)
|
||||
try:
|
||||
response = manager.update(domain_id, name=name,
|
||||
description=description, enabled=enabled)
|
||||
except Exception as e:
|
||||
LOG.exception("Unable to update Domain: %s" % domain_id)
|
||||
raise e
|
||||
return response
|
||||
|
||||
|
||||
def tenant_create(request, name, description=None, enabled=None,
|
||||
@ -223,28 +252,52 @@ def tenant_create(request, name, description=None, enabled=None,
|
||||
raise exceptions.Conflict()
|
||||
|
||||
|
||||
def get_default_domain(request):
|
||||
def get_default_domain(request, get_name=True):
|
||||
"""Gets the default domain object to use when creating Identity object.
|
||||
|
||||
Returns the domain context if is set, otherwise return the domain
|
||||
of the logon user.
|
||||
|
||||
:param get_name: Whether to get the domain name from Keystone if the
|
||||
context isn't set. Setting this to False prevents an unnecessary call
|
||||
to Keystone if only the domain ID is needed.
|
||||
"""
|
||||
domain_id = request.session.get("domain_context", None)
|
||||
domain_name = request.session.get("domain_context_name", None)
|
||||
# if running in Keystone V3 or later
|
||||
if VERSIONS.active >= 3 and not domain_id:
|
||||
# if no domain context set, default to users' domain
|
||||
if VERSIONS.active >= 3 and domain_id is None:
|
||||
# if no domain context set, default to user's domain
|
||||
domain_id = request.user.user_domain_id
|
||||
try:
|
||||
domain = domain_get(request, domain_id)
|
||||
domain_name = domain.name
|
||||
except Exception:
|
||||
LOG.warning("Unable to retrieve Domain: %s" % domain_id)
|
||||
domain_name = request.user.user_domain_name
|
||||
if get_name:
|
||||
try:
|
||||
domain = domain_get(request, domain_id)
|
||||
domain_name = domain.name
|
||||
except Exception:
|
||||
LOG.warning("Unable to retrieve Domain: %s" % domain_id)
|
||||
domain = base.APIDictWrapper({"id": domain_id,
|
||||
"name": domain_name})
|
||||
return domain
|
||||
|
||||
|
||||
def get_effective_domain_id(request):
|
||||
"""Gets the id of the default domain to use when creating Identity objects.
|
||||
If the requests default domain is the same as DEFAULT_DOMAIN, return None.
|
||||
"""
|
||||
domain_id = get_default_domain(request).get('id')
|
||||
return None if domain_id == DEFAULT_DOMAIN else domain_id
|
||||
|
||||
|
||||
def is_cloud_admin(request):
|
||||
return policy.check((("identity", "cloud_admin"),), request)
|
||||
|
||||
|
||||
def is_domain_admin(request):
|
||||
# TODO(btully): check this to verify that domain id is in scope vs target
|
||||
return policy.check(
|
||||
(("identity", "admin_and_matching_domain_id"),), request)
|
||||
|
||||
|
||||
# TODO(gabriel): Is there ever a valid case for admin to be false here?
|
||||
# A quick search through the codebase reveals that it's always called with
|
||||
# admin=true so I suspect we could eliminate it entirely as with the other
|
||||
@ -280,15 +333,17 @@ def tenant_list(request, paginate=False, marker=None, domain=None, user=None,
|
||||
if paginate and len(tenants) > page_size:
|
||||
tenants.pop(-1)
|
||||
has_more_data = True
|
||||
# V3 API
|
||||
else:
|
||||
domain_id = get_effective_domain_id(request)
|
||||
kwargs = {
|
||||
"domain": domain,
|
||||
"domain": domain_id,
|
||||
"user": user
|
||||
}
|
||||
if filters is not None:
|
||||
kwargs.update(filters)
|
||||
tenants = manager.list(**kwargs)
|
||||
return (tenants, has_more_data)
|
||||
return tenants, has_more_data
|
||||
|
||||
|
||||
def tenant_update(request, project, name=None, description=None,
|
||||
@ -514,23 +569,34 @@ def get_project_groups_roles(request, project):
|
||||
groups_roles = collections.defaultdict(list)
|
||||
project_role_assignments = role_assignments_list(request,
|
||||
project=project)
|
||||
|
||||
for role_assignment in project_role_assignments:
|
||||
if not hasattr(role_assignment, 'group'):
|
||||
continue
|
||||
group_id = role_assignment.group['id']
|
||||
role_id = role_assignment.role['id']
|
||||
groups_roles[group_id].append(role_id)
|
||||
|
||||
# filter by project_id
|
||||
if ('project' in role_assignment.scope and
|
||||
role_assignment.scope['project']['id'] == project):
|
||||
groups_roles[group_id].append(role_id)
|
||||
return groups_roles
|
||||
|
||||
|
||||
def role_assignments_list(request, project=None, user=None, role=None,
|
||||
group=None, domain=None, effective=False):
|
||||
group=None, domain=None, effective=False,
|
||||
include_subtree=True):
|
||||
if VERSIONS.active < 3:
|
||||
raise exceptions.NotAvailable
|
||||
|
||||
if include_subtree:
|
||||
domain = None
|
||||
|
||||
manager = keystoneclient(request, admin=True).role_assignments
|
||||
|
||||
return manager.list(project=project, user=user, role=role, group=group,
|
||||
domain=domain, effective=effective)
|
||||
domain=domain, effective=effective,
|
||||
include_subtree=include_subtree)
|
||||
|
||||
|
||||
def role_create(request, name):
|
||||
@ -570,13 +636,18 @@ def roles_for_user(request, user, project=None, domain=None):
|
||||
def get_domain_users_roles(request, domain):
|
||||
users_roles = collections.defaultdict(list)
|
||||
domain_role_assignments = role_assignments_list(request,
|
||||
domain=domain)
|
||||
domain=domain,
|
||||
include_subtree=False)
|
||||
for role_assignment in domain_role_assignments:
|
||||
if not hasattr(role_assignment, 'user'):
|
||||
continue
|
||||
user_id = role_assignment.user['id']
|
||||
role_id = role_assignment.role['id']
|
||||
users_roles[user_id].append(role_id)
|
||||
|
||||
# filter by domain_id
|
||||
if ('domain' in role_assignment.scope and
|
||||
role_assignment.scope['domain']['id'] == domain):
|
||||
users_roles[user_id].append(role_id)
|
||||
return users_roles
|
||||
|
||||
|
||||
@ -609,7 +680,11 @@ def get_project_users_roles(request, project):
|
||||
continue
|
||||
user_id = role_assignment.user['id']
|
||||
role_id = role_assignment.role['id']
|
||||
users_roles[user_id].append(role_id)
|
||||
|
||||
# filter by project_id
|
||||
if ('project' in role_assignment.scope and
|
||||
role_assignment.scope['project']['id'] == project):
|
||||
users_roles[user_id].append(role_id)
|
||||
return users_roles
|
||||
|
||||
|
||||
@ -759,6 +834,11 @@ def get_version():
|
||||
return VERSIONS.active
|
||||
|
||||
|
||||
def is_multi_domain_enabled():
|
||||
return (VERSIONS.active >= 3 and
|
||||
getattr(settings, 'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT', False))
|
||||
|
||||
|
||||
def is_federation_management_enabled():
|
||||
return getattr(settings, 'OPENSTACK_KEYSTONE_FEDERATION_MANAGEMENT', False)
|
||||
|
||||
|
@ -21,6 +21,6 @@ class Admin(horizon.Dashboard):
|
||||
name = _("Admin")
|
||||
slug = "admin"
|
||||
permissions = ('openstack.roles.admin',)
|
||||
|
||||
policy_rules = (("identity", "cloud_admin"),)
|
||||
|
||||
horizon.register(Admin)
|
||||
|
@ -28,3 +28,11 @@ class Domains(horizon.Panel):
|
||||
@staticmethod
|
||||
def can_register():
|
||||
return keystone.VERSIONS.active >= 3
|
||||
|
||||
def can_access(self, context):
|
||||
if keystone.VERSIONS.active < 3:
|
||||
return super(Domains, self).can_access(context)
|
||||
|
||||
request = context['request']
|
||||
domain_token = request.session.get('domain_token')
|
||||
return super(Domains, self).can_access(context) and domain_token
|
||||
|
@ -39,9 +39,7 @@ class UpdateUsersLink(tables.LinkAction):
|
||||
verbose_name = _("Manage Members")
|
||||
url = "horizon:identity:domains:update"
|
||||
classes = ("ajax-modal",)
|
||||
policy_rules = (("identity", "identity:list_users"),
|
||||
("identity", "identity:list_roles"),
|
||||
("identity", "identity:list_role_assignments"))
|
||||
policy_rules = (("identity", "identity:update_domain"),)
|
||||
|
||||
def get_link_url(self, domain):
|
||||
step = 'update_user_members'
|
||||
@ -56,6 +54,7 @@ class UpdateGroupsLink(tables.LinkAction):
|
||||
url = "horizon:identity:domains:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:update_domain"),)
|
||||
|
||||
def get_link_url(self, domain):
|
||||
step = 'update_group_members'
|
||||
@ -227,7 +226,7 @@ class SetDomainContext(tables.Action):
|
||||
verbose_name = _("Set Domain Context")
|
||||
url = constants.DOMAINS_INDEX_URL
|
||||
preempt = True
|
||||
policy_rules = (('identity', 'admin_required'),)
|
||||
policy_rules = (('identity', 'identity:update_domain'),)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
multidomain_support = getattr(settings,
|
||||
@ -262,7 +261,7 @@ class UnsetDomainContext(tables.Action):
|
||||
url = constants.DOMAINS_INDEX_URL
|
||||
preempt = True
|
||||
requires_input = False
|
||||
policy_rules = (('identity', 'admin_required'),)
|
||||
policy_rules = (('identity', 'identity:update_domain'),)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
ctx = request.session.get("domain_context", None)
|
||||
|
@ -269,7 +269,8 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
domain=domain.id) \
|
||||
domain=domain.id,
|
||||
include_subtree=False) \
|
||||
.AndReturn(role_assignments)
|
||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(groups)
|
||||
@ -331,7 +332,8 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
domain=domain.id) \
|
||||
domain=domain.id,
|
||||
include_subtree=False) \
|
||||
.AndReturn(role_assignments)
|
||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(groups)
|
||||
@ -354,15 +356,19 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
# handle
|
||||
api.keystone.domain_update(IsA(http.HttpRequest),
|
||||
domain.id,
|
||||
name=domain.name,
|
||||
description=test_description,
|
||||
domain_id=domain.id,
|
||||
enabled=domain.enabled,
|
||||
name=domain.name).AndReturn(None)
|
||||
enabled=domain.enabled).AndReturn(None)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
domain=domain.id) \
|
||||
domain=domain.id,
|
||||
include_subtree=False) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
domain=domain.id).AndReturn(users)
|
||||
|
||||
# Give user 3 role 1
|
||||
api.keystone.add_domain_user_role(IsA(http.HttpRequest),
|
||||
domain=domain.id,
|
||||
@ -468,7 +474,8 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
domain=domain.id) \
|
||||
domain=domain.id,
|
||||
include_subtree=False) \
|
||||
.AndReturn(role_assignments)
|
||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||
.AndReturn(groups)
|
||||
@ -492,10 +499,10 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
# handle
|
||||
api.keystone.domain_update(IsA(http.HttpRequest),
|
||||
domain.id,
|
||||
name=domain.name,
|
||||
description=test_description,
|
||||
domain_id=domain.id,
|
||||
enabled=domain.enabled,
|
||||
name=domain.name) \
|
||||
enabled=domain.enabled) \
|
||||
.AndRaise(self.exceptions.keystone)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -37,13 +37,13 @@ class IndexView(tables.DataTableView):
|
||||
|
||||
def get_data(self):
|
||||
domains = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
domain_id = api.keystone.get_effective_domain_id(self.request)
|
||||
|
||||
if policy.check((("identity", "identity:list_domains"),),
|
||||
self.request):
|
||||
try:
|
||||
if domain_context:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
domain_context)
|
||||
if domain_id:
|
||||
domain = api.keystone.domain_get(self.request, domain_id)
|
||||
domains.append(domain)
|
||||
else:
|
||||
domains = api.keystone.domain_list(self.request)
|
||||
@ -53,8 +53,7 @@ class IndexView(tables.DataTableView):
|
||||
elif policy.check((("identity", "identity:get_domain"),),
|
||||
self.request):
|
||||
try:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
self.request.user.domain_id)
|
||||
domain = api.keystone.domain_get(self.request, domain_id)
|
||||
domains.append(domain)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
|
@ -322,8 +322,16 @@ class UpdateDomain(workflows.Workflow, IdentityMixIn):
|
||||
users_roles = api.keystone.get_domain_users_roles(request,
|
||||
domain=domain_id)
|
||||
users_to_modify = len(users_roles)
|
||||
all_users = api.keystone.user_list(request,
|
||||
domain=domain_id)
|
||||
users_dict = {user.id: user.name for user in all_users}
|
||||
|
||||
for user_id in users_roles.keys():
|
||||
# Don't remove roles if the user isn't in the domain
|
||||
if user_id not in users_dict:
|
||||
users_to_modify -= 1
|
||||
continue
|
||||
|
||||
# Check if there have been any changes in the roles of
|
||||
# Existing domain members.
|
||||
current_role_ids = list(users_roles[user_id])
|
||||
@ -484,7 +492,7 @@ class UpdateDomain(workflows.Workflow, IdentityMixIn):
|
||||
try:
|
||||
LOG.info('Updating domain with name "%s"' % data['name'])
|
||||
api.keystone.domain_update(request,
|
||||
domain_id=domain_id,
|
||||
domain_id,
|
||||
name=data['name'],
|
||||
description=data['description'],
|
||||
enabled=data['enabled'])
|
||||
|
@ -36,7 +36,7 @@ class CreateGroupForm(forms.SelfHandlingForm):
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.info('Creating group with name "%s"' % data['name'])
|
||||
domain_context = request.session.get('domain_context', None)
|
||||
domain_context = api.keystone.get_effective_domain_id(request)
|
||||
api.keystone.group_create(
|
||||
request,
|
||||
domain_id=domain_context,
|
||||
|
@ -27,3 +27,9 @@ class Groups(horizon.Panel):
|
||||
@staticmethod
|
||||
def can_register():
|
||||
return keystone.VERSIONS.active >= 3
|
||||
|
||||
def can_access(self, context):
|
||||
if keystone.is_multi_domain_enabled() \
|
||||
and not keystone.is_domain_admin(context['request']):
|
||||
return False
|
||||
return super(Groups, self).can_access(context)
|
||||
|
@ -24,6 +24,7 @@ from horizon import tables
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.identity.groups import constants
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -46,7 +47,7 @@ class CreateGroupLink(tables.LinkAction):
|
||||
return api.keystone.keystone_can_edit_group()
|
||||
|
||||
|
||||
class EditGroupLink(tables.LinkAction):
|
||||
class EditGroupLink(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit Group")
|
||||
url = constants.GROUPS_UPDATE_URL
|
||||
@ -58,7 +59,7 @@ class EditGroupLink(tables.LinkAction):
|
||||
return api.keystone.keystone_can_edit_group()
|
||||
|
||||
|
||||
class DeleteGroupsAction(tables.DeleteAction):
|
||||
class DeleteGroupsAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
|
@ -65,11 +65,33 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
||||
self.assertContains(res, 'Edit')
|
||||
self.assertContains(res, 'Delete Group')
|
||||
|
||||
@test.create_stubs({api.keystone: ('group_list',
|
||||
'get_effective_domain_id')})
|
||||
def test_index_with_domain(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
self.setSessionValues(domain_context=domain.id,
|
||||
domain_context_name=domain.name)
|
||||
self.test_index()
|
||||
groups = self._get_groups(domain.id)
|
||||
|
||||
api.keystone.get_effective_domain_id(IgnoreArg()).AndReturn(domain.id)
|
||||
|
||||
api.keystone.group_list(IsA(http.HttpRequest),
|
||||
domain=domain.id).AndReturn(groups)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(GROUPS_INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, constants.GROUPS_INDEX_VIEW_TEMPLATE)
|
||||
self.assertItemsEqual(res.context['table'].data, groups)
|
||||
if domain.id:
|
||||
for group in res.context['table'].data:
|
||||
self.assertItemsEqual(group.domain_id, domain.id)
|
||||
|
||||
self.assertContains(res, 'Create Group')
|
||||
self.assertContains(res, 'Edit')
|
||||
self.assertContains(res, 'Delete Group')
|
||||
|
||||
@test.create_stubs({api.keystone: ('group_list',
|
||||
'keystone_can_edit_group')})
|
||||
@ -159,17 +181,27 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertRedirectsNoFollow(res, GROUPS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.keystone: ('group_get',
|
||||
@test.create_stubs({api.keystone: ('get_effective_domain_id',
|
||||
'group_get',
|
||||
'user_list',)})
|
||||
def test_manage(self):
|
||||
group = self.groups.get(id="1")
|
||||
group_members = self.users.list()
|
||||
domain_id = self._get_domain_id()
|
||||
|
||||
api.keystone.group_get(IsA(http.HttpRequest), group.id).\
|
||||
AndReturn(group)
|
||||
api.keystone.user_list(IgnoreArg(),
|
||||
group=group.id).\
|
||||
AndReturn(group_members)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.get_effective_domain_id(
|
||||
IgnoreArg()).AndReturn(domain_id)
|
||||
api.keystone.user_list(
|
||||
IgnoreArg(), group=group.id, domain=domain_id).AndReturn(
|
||||
group_members)
|
||||
|
||||
else:
|
||||
api.keystone.user_list(
|
||||
IgnoreArg(), group=group.id).AndReturn(group_members)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(GROUP_MANAGE_URL)
|
||||
@ -177,15 +209,25 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
||||
self.assertTemplateUsed(res, constants.GROUPS_MANAGE_VIEW_TEMPLATE)
|
||||
self.assertItemsEqual(res.context['table'].data, group_members)
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list',
|
||||
@test.create_stubs({api.keystone: ('get_effective_domain_id',
|
||||
'user_list',
|
||||
'remove_group_user')})
|
||||
def test_remove_user(self):
|
||||
group = self.groups.get(id="1")
|
||||
user = self.users.get(id="2")
|
||||
domain_id = self._get_domain_id()
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.get_effective_domain_id(
|
||||
IgnoreArg()).AndReturn(domain_id)
|
||||
|
||||
api.keystone.user_list(
|
||||
IgnoreArg(), group=group.id, domain=domain_id).AndReturn(
|
||||
self.users.list())
|
||||
else:
|
||||
api.keystone.user_list(
|
||||
IgnoreArg(), group=group.id).AndReturn(self.users.list())
|
||||
|
||||
api.keystone.user_list(IgnoreArg(),
|
||||
group=group.id).\
|
||||
AndReturn(self.users.list())
|
||||
api.keystone.remove_group_user(IgnoreArg(),
|
||||
group_id=group.id,
|
||||
user_id=user.id)
|
||||
@ -197,20 +239,24 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, GROUP_MANAGE_URL)
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
@test.create_stubs({api.keystone: ('group_get',
|
||||
@test.create_stubs({api.keystone: ('get_effective_domain_id',
|
||||
'group_get',
|
||||
'user_list',
|
||||
'add_group_user')})
|
||||
def test_add_user(self):
|
||||
group = self.groups.get(id="1")
|
||||
user = self.users.get(id="2")
|
||||
domain_id = group.domain_id
|
||||
|
||||
api.keystone.get_effective_domain_id(IgnoreArg()).AndReturn(domain_id)
|
||||
|
||||
api.keystone.group_get(IsA(http.HttpRequest), group.id).\
|
||||
AndReturn(group)
|
||||
api.keystone.user_list(IgnoreArg(),
|
||||
domain=group.domain_id).\
|
||||
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id).\
|
||||
AndReturn(self.users.list())
|
||||
api.keystone.user_list(IgnoreArg(),
|
||||
group=group.id).\
|
||||
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id, group=group.id).\
|
||||
AndReturn(self.users.list()[2:])
|
||||
|
||||
api.keystone.add_group_user(IgnoreArg(),
|
||||
|
@ -39,12 +39,13 @@ class IndexView(tables.DataTableView):
|
||||
|
||||
def get_data(self):
|
||||
groups = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
domain_id = api.keystone.get_effective_domain_id(self.request)
|
||||
|
||||
if policy.check((("identity", "identity:list_groups"),),
|
||||
self.request):
|
||||
try:
|
||||
groups = api.keystone.group_list(self.request,
|
||||
domain=domain_context)
|
||||
domain=domain_id)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve group list.'))
|
||||
@ -108,7 +109,9 @@ class GroupManageMixin(object):
|
||||
@memoized.memoized_method
|
||||
def _get_group_members(self):
|
||||
group_id = self.kwargs['group_id']
|
||||
return api.keystone.user_list(self.request, group=group_id)
|
||||
domain_id = api.keystone.get_effective_domain_id(self.request)
|
||||
return api.keystone.user_list(self.request, domain=domain_id,
|
||||
group=group_id)
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_group_non_members(self):
|
||||
|
@ -62,6 +62,14 @@ class UpdateMembersLink(tables.LinkAction):
|
||||
param = urlencode({"step": step})
|
||||
return "?".join([base_url, param])
|
||||
|
||||
def allowed(self, request, project):
|
||||
if api.keystone.is_multi_domain_enabled():
|
||||
# domain admin or cloud admin = True
|
||||
# project admin or member = False
|
||||
return api.keystone.is_domain_admin(request)
|
||||
else:
|
||||
return super(UpdateMembersLink, self).allowed(request, project)
|
||||
|
||||
|
||||
class UpdateGroupsLink(tables.LinkAction):
|
||||
name = "groups"
|
||||
@ -72,7 +80,12 @@ class UpdateGroupsLink(tables.LinkAction):
|
||||
policy_rules = (("identity", "identity:list_groups"),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return api.keystone.VERSIONS.active >= 3
|
||||
if api.keystone.is_multi_domain_enabled():
|
||||
# domain admin or cloud admin = True
|
||||
# project admin or member = False
|
||||
return api.keystone.is_domain_admin(request)
|
||||
else:
|
||||
return super(UpdateGroupsLink, self).allowed(request, project)
|
||||
|
||||
def get_link_url(self, project):
|
||||
step = 'update_group_members'
|
||||
@ -101,19 +114,30 @@ class CreateProject(tables.LinkAction):
|
||||
policy_rules = (('identity', 'identity:create_project'),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
if api.keystone.is_multi_domain_enabled():
|
||||
# domain admin or cloud admin = True
|
||||
# project admin or member = False
|
||||
return api.keystone.is_domain_admin(request)
|
||||
else:
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
|
||||
|
||||
class UpdateProject(tables.LinkAction):
|
||||
class UpdateProject(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Edit Project")
|
||||
url = "horizon:identity:projects:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (('identity', 'identity:update_project'),)
|
||||
policy_target_attrs = (("target.project.domain_id", "domain_id"),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
if api.keystone.is_multi_domain_enabled():
|
||||
# domain admin or cloud admin = True
|
||||
# project admin or member = False
|
||||
return api.keystone.is_domain_admin(request)
|
||||
else:
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
|
||||
|
||||
class ModifyQuotas(tables.LinkAction):
|
||||
@ -124,6 +148,12 @@ class ModifyQuotas(tables.LinkAction):
|
||||
icon = "pencil"
|
||||
policy_rules = (('compute', "compute_extension:quotas:update"),)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
if api.keystone.VERSIONS.active < 3:
|
||||
return True
|
||||
else:
|
||||
return api.keystone.is_cloud_admin(request)
|
||||
|
||||
def get_link_url(self, project):
|
||||
step = 'update_quotas'
|
||||
base_url = reverse(self.url, args=[project.id])
|
||||
@ -131,7 +161,7 @@ class ModifyQuotas(tables.LinkAction):
|
||||
return "?".join([base_url, param])
|
||||
|
||||
|
||||
class DeleteTenantsAction(tables.DeleteAction):
|
||||
class DeleteTenantsAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
@ -149,8 +179,12 @@ class DeleteTenantsAction(tables.DeleteAction):
|
||||
)
|
||||
|
||||
policy_rules = (("identity", "identity:delete_project"),)
|
||||
policy_target_attrs = ("target.project.domain_id", "domain_id"),
|
||||
|
||||
def allowed(self, request, project):
|
||||
if api.keystone.is_multi_domain_enabled() \
|
||||
and not api.keystone.is_domain_admin(request):
|
||||
return False
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
@ -238,6 +272,17 @@ class TenantsTable(tables.DataTable):
|
||||
required=False),
|
||||
update_action=UpdateCell)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
domain_name = tables.Column(
|
||||
'domain_name', verbose_name=_('Domain Name'))
|
||||
enabled = tables.Column('enabled', verbose_name=_('Enabled'),
|
||||
status=True,
|
||||
filters=(filters.yesno, filters.capfirst),
|
||||
form_field=forms.BooleanField(
|
||||
label=_('Enabled'),
|
||||
required=False),
|
||||
update_action=UpdateCell)
|
||||
|
||||
def get_project_detail_link(self, project):
|
||||
# this method is an ugly monkey patch, needed because
|
||||
# the column link method does not provide access to the request
|
||||
|
@ -51,31 +51,43 @@ PROJECT_DETAIL_URL = reverse('horizon:identity:projects:detail', args=[1])
|
||||
|
||||
|
||||
class TenantsViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
@test.create_stubs({api.keystone: ('tenant_list', 'domain_lookup')})
|
||||
def test_index(self):
|
||||
domain = self.domains.get(id="1")
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
domain=None,
|
||||
paginate=True,
|
||||
marker=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
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())
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_list', )})
|
||||
@test.create_stubs({api.keystone: ('tenant_list',
|
||||
'get_effective_domain_id',
|
||||
'domain_lookup')})
|
||||
def test_index_with_domain_context(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
self.setSessionValues(domain_context=domain.id,
|
||||
domain_context_name=domain.name)
|
||||
|
||||
domain_tenants = [tenant for tenant in self.tenants.list()
|
||||
if tenant.domain_id == domain.id]
|
||||
|
||||
api.keystone.get_effective_domain_id(IgnoreArg()).AndReturn(domain.id)
|
||||
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
domain=domain.id,
|
||||
paginate=True,
|
||||
marker=None) \
|
||||
.AndReturn([domain_tenants, False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
@ -86,14 +98,18 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
||||
|
||||
class ProjectsViewNonAdminTests(test.TestCase):
|
||||
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check)
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
@test.create_stubs({api.keystone: ('tenant_list',
|
||||
'domain_lookup')})
|
||||
def test_index(self):
|
||||
domain = self.domains.get(id="1")
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||
user=self.user.id,
|
||||
paginate=True,
|
||||
marker=None,
|
||||
admin=False) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
@ -393,6 +409,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
'role_list',
|
||||
'group_list',
|
||||
'get_default_domain',
|
||||
'is_cloud_admin',
|
||||
'get_default_role'),
|
||||
quotas: ('get_default_quota_data',
|
||||
'get_disabled_quotas')})
|
||||
@ -407,8 +424,13 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# init
|
||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||
.AndReturn(default_domain)
|
||||
|
||||
api.keystone.is_cloud_admin(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
|
||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.disabled_quotas.first())
|
||||
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)) \
|
||||
.AndRaise(self.exceptions.nova)
|
||||
|
||||
@ -456,7 +478,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
# init
|
||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||
.AndReturn(default_domain)
|
||||
.MultipleTimes().AndReturn(default_domain)
|
||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.disabled_quotas.first())
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
@ -515,7 +537,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
# init
|
||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||
.AndReturn(default_domain)
|
||||
.MultipleTimes().AndReturn(default_domain)
|
||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.disabled_quotas.first())
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
@ -600,8 +622,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||
.AndReturn(default_domain)
|
||||
api.keystone.get_default_domain(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(default_domain)
|
||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.disabled_quotas.first())
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
@ -772,7 +794,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
workflow_data[GROUP_ROLE_PREFIX + "2"] = ['1', '2', '3']
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
.MultipleTimes().AndReturn(role_assignments)
|
||||
# Give user 1 role 2
|
||||
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
|
||||
project=self.tenant.id,
|
||||
@ -879,7 +901,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
if keystone_api_version >= 3:
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
.MultipleTimes().AndReturn(role_assignments)
|
||||
else:
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
@ -890,10 +912,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
user.id,
|
||||
self.tenant.id).AndReturn(roles)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:identity:projects:update',
|
||||
@ -922,6 +940,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_get',
|
||||
'domain_get',
|
||||
'get_effective_domain_id',
|
||||
'tenant_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
@ -938,7 +957,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
api.cinder: ('tenant_quota_update',),
|
||||
quotas: ('get_tenant_quota_data',
|
||||
'get_disabled_quotas',
|
||||
'tenant_quota_usages')})
|
||||
'tenant_quota_usages',)})
|
||||
def test_update_project_save(self, neutron=False):
|
||||
keystone_api_version = api.keystone.VERSIONS.active
|
||||
|
||||
@ -979,11 +998,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data = {}
|
||||
|
||||
if keystone_api_version >= 3:
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
else:
|
||||
if keystone_api_version < 3:
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(proj_users)
|
||||
@ -993,10 +1008,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
user.id,
|
||||
self.tenant.id).AndReturn(roles)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
workflow_data[USER_ROLE_PREFIX + "1"] = ['3'] # admin role
|
||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['2'] # member role
|
||||
# Group assignment form data
|
||||
@ -1010,16 +1021,22 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
quota.metadata_items = 444
|
||||
quota.volumes = 444
|
||||
|
||||
updated_project = {"name": project._info["name"],
|
||||
"description": project._info["description"],
|
||||
"enabled": project.enabled}
|
||||
updated_quota = self._get_quota_info(quota)
|
||||
|
||||
# called once for tenant_update
|
||||
api.keystone.get_effective_domain_id(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||
|
||||
# handle
|
||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_project) \
|
||||
.AndReturn(project)
|
||||
name=project._info["name"],
|
||||
description=project._info['description'],
|
||||
enabled=project.enabled,
|
||||
domain=domain_id).AndReturn(project)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
domain=domain_id).AndReturn(users)
|
||||
|
||||
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||
proj_users, roles, workflow_data)
|
||||
@ -1092,6 +1109,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_get',
|
||||
'domain_get',
|
||||
'get_effective_domain_id',
|
||||
'tenant_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
@ -1148,7 +1166,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
if keystone_api_version >= 3:
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
.MultipleTimes().AndReturn(role_assignments)
|
||||
else:
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
@ -1164,10 +1182,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
workflow_data.setdefault(USER_ROLE_PREFIX + role_ids[0], []) \
|
||||
.append(user.id)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
role_ids = [role.id for role in roles]
|
||||
for group in groups:
|
||||
if role_ids:
|
||||
@ -1181,17 +1195,21 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
quota.metadata_items = 444
|
||||
quota.volumes = 444
|
||||
|
||||
updated_project = {"name": project._info["name"],
|
||||
"description": project._info["description"],
|
||||
"enabled": project.enabled}
|
||||
updated_quota = self._get_quota_info(quota)
|
||||
|
||||
# handle
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
api.keystone.get_effective_domain_id(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||
|
||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_project) \
|
||||
name=project._info["name"],
|
||||
domain=domain_id,
|
||||
description=project._info['description'],
|
||||
enabled=project.enabled) \
|
||||
.AndRaise(self.exceptions.keystone)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -1213,6 +1231,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_get',
|
||||
'domain_get',
|
||||
'get_effective_domain_id',
|
||||
'tenant_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
@ -1257,8 +1276,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
domain=domain_id).AndReturn(users)
|
||||
|
||||
api.keystone.role_list(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(roles)
|
||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||
@ -1266,24 +1287,16 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data = {}
|
||||
|
||||
if keystone_api_version >= 3:
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
else:
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(proj_users)
|
||||
if keystone_api_version < 3:
|
||||
api.keystone.user_list(
|
||||
IsA(http.HttpRequest),
|
||||
project=self.tenant.id).AndReturn(proj_users)
|
||||
|
||||
for user in proj_users:
|
||||
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
||||
user.id,
|
||||
self.tenant.id).AndReturn(roles)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
workflow_data[USER_ROLE_PREFIX + "1"] = ['1', '3'] # admin role
|
||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['1', '2', '3'] # member role
|
||||
# Group role assignment data
|
||||
@ -1297,16 +1310,21 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
quota[0].limit = 444
|
||||
quota[1].limit = -1
|
||||
|
||||
updated_project = {"name": project._info["name"],
|
||||
"description": project._info["description"],
|
||||
"enabled": project.enabled}
|
||||
updated_quota = self._get_quota_info(quota)
|
||||
|
||||
# handle
|
||||
api.keystone.get_effective_domain_id(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||
|
||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_project) \
|
||||
.AndReturn(project)
|
||||
name=project._info["name"],
|
||||
description=project._info['description'],
|
||||
enabled=project.enabled,
|
||||
domain=domain_id).AndReturn(project)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
domain=domain_id).AndReturn(users)
|
||||
|
||||
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||
proj_users, roles, workflow_data)
|
||||
@ -1352,7 +1370,8 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
'add_group_role',
|
||||
'group_list',
|
||||
'role_list',
|
||||
'role_assignments_list'),
|
||||
'role_assignments_list',
|
||||
'get_effective_domain_id'),
|
||||
quotas: ('get_tenant_quota_data',
|
||||
'get_disabled_quotas',
|
||||
'tenant_quota_usages')})
|
||||
@ -1384,8 +1403,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(default_role)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
|
||||
api.keystone.role_list(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(roles)
|
||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||
@ -1393,24 +1414,16 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
|
||||
workflow_data = {}
|
||||
|
||||
if keystone_api_version >= 3:
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
else:
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(proj_users)
|
||||
if keystone_api_version < 3:
|
||||
api.keystone.user_list(
|
||||
IsA(http.HttpRequest),
|
||||
project=self.tenant.id).AndReturn(proj_users)
|
||||
|
||||
for user in proj_users:
|
||||
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
||||
user.id,
|
||||
self.tenant.id).AndReturn(roles)
|
||||
|
||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||
project=self.tenant.id) \
|
||||
.AndReturn(role_assignments)
|
||||
|
||||
workflow_data[USER_ROLE_PREFIX + "1"] = ['1', '3'] # admin role
|
||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['1', '2', '3'] # member role
|
||||
|
||||
@ -1423,21 +1436,28 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
quota.metadata_items = 444
|
||||
quota.volumes = 444
|
||||
|
||||
updated_project = {"name": project._info["name"],
|
||||
"description": project._info["description"],
|
||||
"enabled": project.enabled}
|
||||
updated_quota = self._get_quota_info(quota)
|
||||
|
||||
# handle
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
api.keystone.get_effective_domain_id(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||
|
||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_project) \
|
||||
.AndReturn(project)
|
||||
name=project._info["name"],
|
||||
description=project._info['description'],
|
||||
enabled=project.enabled,
|
||||
domain=domain_id).AndReturn(project)
|
||||
|
||||
api.keystone.user_list(IsA(http.HttpRequest),
|
||||
domain=domain_id).AndReturn(users)
|
||||
|
||||
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||
proj_users, roles, workflow_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# submit form data
|
||||
@ -1592,7 +1612,8 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
|
||||
"The WITH_SELENIUM env variable is not set.")
|
||||
class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
@test.create_stubs(
|
||||
{api.keystone: ('tenant_list', 'tenant_get', 'tenant_update')})
|
||||
{api.keystone: ('tenant_list', 'tenant_get', 'tenant_update',
|
||||
'domain_lookup')})
|
||||
def test_inline_editing_update(self):
|
||||
# Tenant List
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
@ -1600,6 +1621,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
marker=None,
|
||||
paginate=True) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||
# Edit mod
|
||||
api.keystone.tenant_get(IgnoreArg(),
|
||||
u'1',
|
||||
@ -1673,7 +1695,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
"'Changed test_tenant'")
|
||||
|
||||
@test.create_stubs(
|
||||
{api.keystone: ('tenant_list', 'tenant_get')})
|
||||
{api.keystone: ('tenant_list', 'tenant_get', 'domain_lookup')})
|
||||
def test_inline_editing_cancel(self):
|
||||
# Tenant List
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
@ -1681,6 +1703,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
marker=None,
|
||||
paginate=True) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||
# Edit mod
|
||||
api.keystone.tenant_get(IgnoreArg(),
|
||||
u'1',
|
||||
|
@ -77,10 +77,12 @@ class IndexView(tables.DataTableView):
|
||||
tenants = []
|
||||
marker = self.request.GET.get(
|
||||
project_tables.TenantsTable._meta.pagination_param, None)
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
|
||||
self._more = False
|
||||
|
||||
if policy.check((("identity", "identity:list_projects"),),
|
||||
self.request):
|
||||
domain_context = api.keystone.get_effective_domain_id(self.request)
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
@ -106,6 +108,11 @@ class IndexView(tables.DataTableView):
|
||||
msg = \
|
||||
_("Insufficient privilege level to view project information.")
|
||||
messages.info(self.request, msg)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
domain_lookup = api.keystone.domain_lookup(self.request)
|
||||
for t in tenants:
|
||||
t.domain_name = domain_lookup.get(t.domain_id)
|
||||
return tenants
|
||||
|
||||
|
||||
@ -126,6 +133,11 @@ class CreateProjectView(workflows.WorkflowView):
|
||||
workflow_class = project_workflows.CreateProject
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
if (api.keystone.is_multi_domain_enabled() and
|
||||
not api.keystone.is_cloud_admin(self.request)):
|
||||
self.workflow_class = project_workflows.CreateProjectNoQuota
|
||||
|
||||
initial = super(CreateProjectView, self).get_initial()
|
||||
|
||||
# Set the domain of the project
|
||||
@ -133,33 +145,36 @@ class CreateProjectView(workflows.WorkflowView):
|
||||
initial["domain_id"] = domain.id
|
||||
initial["domain_name"] = domain.name
|
||||
|
||||
# TODO(esp): fix this for Domain Admin or find a work around
|
||||
# get initial quota defaults
|
||||
try:
|
||||
quota_defaults = quotas.get_default_quota_data(self.request)
|
||||
|
||||
if api.keystone.is_cloud_admin(self.request):
|
||||
try:
|
||||
if api.base.is_service_enabled(self.request, 'network') and \
|
||||
api.neutron.is_quotas_extension_supported(
|
||||
self.request):
|
||||
# TODO(jpichon): There is no API to access the Neutron
|
||||
# default quotas (LP#1204956). For now, use the values
|
||||
# from the current project.
|
||||
project_id = self.request.user.project_id
|
||||
quota_defaults += api.neutron.tenant_quota_get(
|
||||
self.request,
|
||||
tenant_id=project_id)
|
||||
quota_defaults = quotas.get_default_quota_data(self.request)
|
||||
|
||||
try:
|
||||
if api.base.is_service_enabled(
|
||||
self.request, 'network') and \
|
||||
api.neutron.is_quotas_extension_supported(
|
||||
self.request):
|
||||
# TODO(jpichon): There is no API to access the Neutron
|
||||
# default quotas (LP#1204956). For now, use the values
|
||||
# from the current project.
|
||||
project_id = self.request.user.project_id
|
||||
quota_defaults += api.neutron.tenant_quota_get(
|
||||
self.request,
|
||||
tenant_id=project_id)
|
||||
except Exception:
|
||||
error_msg = _('Unable to retrieve default Neutron quota '
|
||||
'values.')
|
||||
self.add_error_to_step(error_msg, 'create_quotas')
|
||||
|
||||
for field in quotas.QUOTA_FIELDS:
|
||||
initial[field] = quota_defaults.get(field).limit
|
||||
|
||||
except Exception:
|
||||
error_msg = _('Unable to retrieve default Neutron quota '
|
||||
'values.')
|
||||
error_msg = _('Unable to retrieve default quota values.')
|
||||
self.add_error_to_step(error_msg, 'create_quotas')
|
||||
|
||||
for field in quotas.QUOTA_FIELDS:
|
||||
initial[field] = quota_defaults.get(field).limit
|
||||
|
||||
except Exception:
|
||||
error_msg = _('Unable to retrieve default quota values.')
|
||||
self.add_error_to_step(error_msg, 'create_quotas')
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
@ -167,6 +182,11 @@ class UpdateProjectView(workflows.WorkflowView):
|
||||
workflow_class = project_workflows.UpdateProject
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
if (api.keystone.is_multi_domain_enabled() and
|
||||
not api.keystone.is_cloud_admin(self.request)):
|
||||
self.workflow_class = project_workflows.UpdateProjectNoQuota
|
||||
|
||||
initial = super(UpdateProjectView, self).get_initial()
|
||||
|
||||
project_id = self.kwargs['tenant_id']
|
||||
@ -182,23 +202,32 @@ class UpdateProjectView(workflows.WorkflowView):
|
||||
# Retrieve the domain name where the project belong
|
||||
if keystone.VERSIONS.active >= 3:
|
||||
try:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
initial["domain_id"])
|
||||
initial["domain_name"] = domain.name
|
||||
if policy.check((("identity", "identity:get_domain"),),
|
||||
self.request):
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
initial["domain_id"])
|
||||
initial["domain_name"] = domain.name
|
||||
|
||||
else:
|
||||
domain = api.keystone.get_default_domain(self.request)
|
||||
initial["domain_name"] = domain.name
|
||||
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project domain.'),
|
||||
redirect=reverse(INDEX_URL))
|
||||
|
||||
# get initial project quota
|
||||
quota_data = quotas.get_tenant_quota_data(self.request,
|
||||
tenant_id=project_id)
|
||||
if api.base.is_service_enabled(self.request, 'network') and \
|
||||
api.neutron.is_quotas_extension_supported(self.request):
|
||||
quota_data += api.neutron.tenant_quota_get(
|
||||
self.request, tenant_id=project_id)
|
||||
for field in quotas.QUOTA_FIELDS:
|
||||
initial[field] = quota_data.get(field).limit
|
||||
if keystone.is_cloud_admin(self.request):
|
||||
quota_data = quotas.get_tenant_quota_data(self.request,
|
||||
tenant_id=project_id)
|
||||
if api.base.is_service_enabled(self.request, 'network') and \
|
||||
api.neutron.is_quotas_extension_supported(
|
||||
self.request):
|
||||
quota_data += api.neutron.tenant_quota_get(
|
||||
self.request, tenant_id=project_id)
|
||||
for field in quotas.QUOTA_FIELDS:
|
||||
initial[field] = quota_data.get(field).limit
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project details.'),
|
||||
|
@ -16,6 +16,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -34,6 +35,9 @@ from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.usage import quotas
|
||||
from openstack_dashboard.utils.identity import IdentityMixIn
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
INDEX_URL = "horizon:identity:projects:index"
|
||||
ADD_USER_URL = "horizon:identity:projects:create_user"
|
||||
PROJECT_GROUP_ENABLED = keystone.VERSIONS.active >= 3
|
||||
@ -111,6 +115,7 @@ class UpdateProjectQuotaAction(ProjectQuotaAction):
|
||||
name = _("Quota")
|
||||
slug = 'update_quotas'
|
||||
help_text = _("Set maximum quotas for the project.")
|
||||
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||
|
||||
|
||||
class CreateProjectQuotaAction(ProjectQuotaAction):
|
||||
@ -118,6 +123,7 @@ class CreateProjectQuotaAction(ProjectQuotaAction):
|
||||
name = _("Quota")
|
||||
slug = 'create_quotas'
|
||||
help_text = _("Set maximum quotas for the project.")
|
||||
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||
|
||||
|
||||
class UpdateProjectQuota(workflows.Step):
|
||||
@ -186,13 +192,14 @@ class UpdateProjectMembersAction(workflows.MembershipAction):
|
||||
err_msg = _('Unable to retrieve user list. Please try again later.')
|
||||
# Use the domain_id from the project
|
||||
domain_id = self.initial.get("domain_id", None)
|
||||
|
||||
project_id = ''
|
||||
if 'project_id' in self.initial:
|
||||
project_id = self.initial['project_id']
|
||||
|
||||
# Get the default role
|
||||
try:
|
||||
default_role = api.keystone.get_default_role(self.request)
|
||||
default_role = keystone.get_default_role(self.request)
|
||||
# Default role is necessary to add members to a project
|
||||
if default_role is None:
|
||||
default = getattr(settings,
|
||||
@ -528,12 +535,38 @@ class CreateProject(CommonQuotaWorkflow):
|
||||
self._update_project_members(request, data, project_id)
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self._update_project_groups(request, data, project_id)
|
||||
self._update_project_quota(request, data, project_id)
|
||||
if keystone.is_cloud_admin(request):
|
||||
self._update_project_quota(request, data, project_id)
|
||||
return True
|
||||
|
||||
|
||||
class CreateProjectNoQuota(CreateProject):
|
||||
slug = "create_project"
|
||||
name = _("Create Project")
|
||||
finalize_button_name = _("Create Project")
|
||||
success_message = _('Created new project "%s".')
|
||||
failure_message = _('Unable to create project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (CreateProjectInfo, UpdateProjectMembers)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (CreateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups,)
|
||||
super(CreateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
entry_point=entry_point,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
|
||||
class UpdateProjectInfoAction(CreateProjectInfoAction):
|
||||
enabled = forms.BooleanField(required=False, label=_("Enabled"))
|
||||
domain_name = forms.CharField(label=_("Domain Name"),
|
||||
required=False,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
def __init__(self, request, initial, *args, **kwargs):
|
||||
super(UpdateProjectInfoAction, self).__init__(
|
||||
@ -608,7 +641,8 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
||||
return api.keystone.role_list(request)
|
||||
|
||||
def _update_project(self, request, data):
|
||||
# update project info
|
||||
"""Update project info"""
|
||||
domain_id = api.keystone.get_effective_domain_id(self.request)
|
||||
try:
|
||||
project_id = data['project_id']
|
||||
return api.keystone.tenant_update(
|
||||
@ -616,12 +650,14 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
||||
project_id,
|
||||
name=data['name'],
|
||||
description=data['description'],
|
||||
enabled=data['enabled'])
|
||||
enabled=data['enabled'],
|
||||
domain=domain_id)
|
||||
except exceptions.Conflict:
|
||||
msg = _('Project name "%s" is already used.') % data['name']
|
||||
self.failure_message = msg
|
||||
return
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
LOG.debug('Project update failed: %s' % e)
|
||||
exceptions.handle(request, ignore=True)
|
||||
return
|
||||
|
||||
@ -699,7 +735,22 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
||||
request, project=project_id)
|
||||
users_to_modify = len(users_roles)
|
||||
|
||||
# TODO(bpokorny): The following lines are needed to make sure we
|
||||
# only modify roles for users who are in the current domain.
|
||||
# Otherwise, we'll end up removing roles for users who have roles
|
||||
# on the project but aren't in the domain. For now, Horizon won't
|
||||
# support managing roles across domains. The Keystone CLI
|
||||
# supports it, so we may want to add that in the future.
|
||||
all_users = api.keystone.user_list(request,
|
||||
domain=data['domain_id'])
|
||||
users_dict = {user.id: user.name for user in all_users}
|
||||
|
||||
for user_id in users_roles.keys():
|
||||
# Don't remove roles if the user isn't in the domain
|
||||
if user_id not in users_dict:
|
||||
users_to_modify -= 1
|
||||
continue
|
||||
|
||||
# Check if there have been any changes in the roles of
|
||||
# Existing project members.
|
||||
current_role_ids = list(users_roles[user_id])
|
||||
@ -854,8 +905,32 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
||||
if not ret:
|
||||
return False
|
||||
|
||||
ret = self._update_project_quota(request, data, project_id)
|
||||
if not ret:
|
||||
return False
|
||||
if api.keystone.is_cloud_admin(request):
|
||||
ret = self._update_project_quota(request, data, project_id)
|
||||
if not ret:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class UpdateProjectNoQuota(UpdateProject):
|
||||
slug = "update_project"
|
||||
name = _("Edit Project")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Modified project "%s".')
|
||||
failure_message = _('Unable to modify project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (UpdateProjectInfo, UpdateProjectMembers)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (UpdateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups)
|
||||
|
||||
super(UpdateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
entry_point=entry_point,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
@ -24,6 +24,12 @@ class Roles(horizon.Panel):
|
||||
slug = 'roles'
|
||||
policy_rules = (("identity", "identity:list_roles"),)
|
||||
|
||||
def can_access(self, context):
|
||||
if keystone.is_multi_domain_enabled() \
|
||||
and not keystone.is_domain_admin(context['request']):
|
||||
return False
|
||||
return super(Roles, self).can_access(context)
|
||||
|
||||
@staticmethod
|
||||
def can_register():
|
||||
return keystone.VERSIONS.active >= 3
|
||||
|
@ -68,17 +68,26 @@ class BaseUserForm(forms.SelfHandlingForm):
|
||||
# the user has access to.
|
||||
user_id = kwargs['initial'].get('id', None)
|
||||
domain_id = kwargs['initial'].get('domain_id', None)
|
||||
projects, has_more = api.keystone.tenant_list(request,
|
||||
domain=domain_id,
|
||||
user=user_id)
|
||||
for project in projects:
|
||||
if project.enabled:
|
||||
project_choices.append((project.id, project.name))
|
||||
if not project_choices:
|
||||
project_choices.insert(0, ('', _("No available projects")))
|
||||
elif len(project_choices) > 1:
|
||||
project_choices.insert(0, ('', _("Select a project")))
|
||||
self.fields['project'].choices = project_choices
|
||||
|
||||
try:
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
projects, has_more = api.keystone.tenant_list(
|
||||
request, domain=domain_id)
|
||||
else:
|
||||
projects, has_more = api.keystone.tenant_list(
|
||||
request, user=user_id)
|
||||
|
||||
for project in projects:
|
||||
if project.enabled:
|
||||
project_choices.append((project.id, project.name))
|
||||
if not project_choices:
|
||||
project_choices.insert(0, ('', _("No available projects")))
|
||||
elif len(project_choices) > 1:
|
||||
project_choices.insert(0, ('', _("Select a project")))
|
||||
self.fields['project'].choices = project_choices
|
||||
|
||||
except Exception:
|
||||
LOG.debug("User: %s has no projects" % user_id)
|
||||
|
||||
|
||||
ADD_PROJECT_URL = "horizon:identity:projects:create"
|
||||
@ -135,7 +144,7 @@ class CreateUserForm(PasswordMixin, BaseUserForm):
|
||||
# password and confirm_password strings.
|
||||
@sensitive_variables('data')
|
||||
def handle(self, request, data):
|
||||
domain = api.keystone.get_default_domain(self.request)
|
||||
domain = api.keystone.get_default_domain(self.request, False)
|
||||
try:
|
||||
LOG.info('Creating user with name "%s"' % data['name'])
|
||||
desc = data["description"]
|
||||
|
@ -20,9 +20,17 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.api import keystone
|
||||
|
||||
|
||||
class Users(horizon.Panel):
|
||||
name = _("Users")
|
||||
slug = 'users'
|
||||
policy_rules = (("identity", "identity:get_user"),
|
||||
("identity", "identity:list_users"))
|
||||
|
||||
def can_access(self, context):
|
||||
if keystone.is_multi_domain_enabled() \
|
||||
and not keystone.is_domain_admin(context['request']):
|
||||
return False
|
||||
return super(Users, self).can_access(context)
|
||||
|
@ -50,7 +50,8 @@ class EditUserLink(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
icon = "pencil"
|
||||
policy_rules = (("identity", "identity:update_user"),
|
||||
("identity", "identity:list_projects"),)
|
||||
policy_target_attrs = (("user_id", "id"),)
|
||||
policy_target_attrs = (("user_id", "id"),
|
||||
("target.user.domain_id", "domain_id"),)
|
||||
|
||||
def allowed(self, request, user):
|
||||
return api.keystone.keystone_can_edit_user()
|
||||
@ -103,7 +104,8 @@ class ToggleEnabled(policy.PolicyTargetMixin, tables.BatchAction):
|
||||
)
|
||||
classes = ("btn-toggle",)
|
||||
policy_rules = (("identity", "identity:update_user"),)
|
||||
policy_target_attrs = (("user_id", "id"),)
|
||||
policy_target_attrs = (("user_id", "id"),
|
||||
("target.user.domain_id", "domain_id"))
|
||||
|
||||
def allowed(self, request, user=None):
|
||||
if not api.keystone.keystone_can_edit_user():
|
||||
@ -137,7 +139,7 @@ class ToggleEnabled(policy.PolicyTargetMixin, tables.BatchAction):
|
||||
self.current_past_action = ENABLE
|
||||
|
||||
|
||||
class DeleteUsersAction(tables.DeleteAction):
|
||||
class DeleteUsersAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
@ -256,6 +258,18 @@ class UsersTable(tables.DataTable):
|
||||
defaultfilters.capfirst),
|
||||
empty_value="False")
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
domain_name = tables.Column(
|
||||
'domain_name',
|
||||
verbose_name=_('Domain Name'),
|
||||
attrs={'data-type': 'uuid'})
|
||||
enabled = tables.Column('enabled', verbose_name=_('Enabled'),
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES,
|
||||
filters=(defaultfilters.yesno,
|
||||
defaultfilters.capfirst),
|
||||
empty_value="False")
|
||||
|
||||
class Meta(object):
|
||||
name = "users"
|
||||
verbose_name = _("Users")
|
||||
|
@ -53,13 +53,20 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
if user.domain_id == domain_id]
|
||||
return users
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list',)})
|
||||
@test.create_stubs({api.keystone: ('user_list',
|
||||
'get_effective_domain_id',
|
||||
'domain_lookup')})
|
||||
def test_index(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
users = self._get_users(domain_id)
|
||||
|
||||
api.keystone.get_effective_domain_id(IgnoreArg()).AndReturn(domain_id)
|
||||
|
||||
api.keystone.user_list(IgnoreArg(),
|
||||
domain=domain_id).AndReturn(users)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res = self.client.get(USERS_INDEX_URL)
|
||||
@ -91,11 +98,19 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
role = self.roles.first()
|
||||
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
domain=domain_id,
|
||||
user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
.AndReturn(domain)
|
||||
api.keystone.get_default_domain(IgnoreArg(), False) \
|
||||
.AndReturn(domain)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.user_create(IgnoreArg(),
|
||||
name=user.name,
|
||||
description=user.description,
|
||||
@ -146,11 +161,19 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
domain_id = domain.id
|
||||
role = self.roles.first()
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
domain=domain_id,
|
||||
user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
.AndReturn(domain)
|
||||
api.keystone.get_default_domain(IgnoreArg(), False) \
|
||||
.AndReturn(domain)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=user.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.user_create(IgnoreArg(),
|
||||
name=user.name,
|
||||
description=user.description,
|
||||
@ -192,8 +215,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain_id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||
api.keystone.get_default_role(IgnoreArg()) \
|
||||
.AndReturn(self.roles.first())
|
||||
@ -224,8 +255,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain_id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||
api.keystone.get_default_role(IgnoreArg()) \
|
||||
.AndReturn(self.roles.first())
|
||||
@ -259,8 +298,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain_id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||
api.keystone.get_default_role(IgnoreArg()) \
|
||||
.AndReturn(self.roles.first())
|
||||
@ -299,10 +346,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
admin=True).AndReturn(user)
|
||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||
domain_id).AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
domain=domain_id,
|
||||
user=user.id) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=user.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.user_update(IsA(http.HttpRequest),
|
||||
user.id,
|
||||
email=user.email,
|
||||
@ -337,10 +390,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
admin=True).AndReturn(user)
|
||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||
domain_id).AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
domain=domain_id,
|
||||
user=user.id) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain_id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=user.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.user_update(IsA(http.HttpRequest),
|
||||
user.id,
|
||||
email=user.email,
|
||||
@ -376,8 +435,15 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
admin=True).AndReturn(user)
|
||||
api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \
|
||||
.AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=user.id) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain_id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=user.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.keystone_can_edit_user().AndReturn(False)
|
||||
api.keystone.keystone_can_edit_user().AndReturn(False)
|
||||
|
||||
@ -486,7 +552,9 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
res, "form", 'password',
|
||||
['Password must be between 8 and 18 characters.'])
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')})
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled',
|
||||
'user_list',
|
||||
'domain_lookup')})
|
||||
def test_enable_user(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
@ -498,6 +566,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
api.keystone.user_update_enabled(IgnoreArg(),
|
||||
user.id,
|
||||
True).AndReturn(user)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -506,7 +576,9 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')})
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled',
|
||||
'user_list',
|
||||
'domain_lookup')})
|
||||
def test_disable_user(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
@ -520,6 +592,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
api.keystone.user_update_enabled(IgnoreArg(),
|
||||
user.id,
|
||||
False).AndReturn(user)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -528,7 +602,9 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')})
|
||||
@test.create_stubs({api.keystone: ('user_update_enabled',
|
||||
'user_list',
|
||||
'domain_lookup')})
|
||||
def test_enable_disable_user_exception(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
@ -540,6 +616,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
.AndReturn(users)
|
||||
api.keystone.user_update_enabled(IgnoreArg(), user.id, True) \
|
||||
.AndRaise(self.exceptions.keystone)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
formData = {'action': 'users__toggle__%s' % user.id}
|
||||
@ -547,7 +625,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list',)})
|
||||
@test.create_stubs({api.keystone: ('user_list', 'domain_lookup')})
|
||||
def test_disabling_current_user(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
@ -555,6 +633,33 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
for i in range(0, 2):
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
formData = {'action': 'users__toggle__%s' % self.request.user.id}
|
||||
res = self.client.post(USERS_INDEX_URL, formData, follow=True)
|
||||
|
||||
self.assertEqual(list(res.context['messages'])[0].message,
|
||||
u'You cannot disable the user you are currently '
|
||||
u'logged in as.')
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list', 'domain_lookup')})
|
||||
def test_disabling_current_user_domain_name(self):
|
||||
domain = self._get_default_domain()
|
||||
domains = self.domains.list()
|
||||
domain_id = domain.id
|
||||
users = self._get_users(domain_id)
|
||||
domain_lookup = dict((d.id, d.name) for d in domains)
|
||||
|
||||
for u in users:
|
||||
u.domain_name = domain_lookup.get(u.domain_id)
|
||||
|
||||
for i in range(0, 2):
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn(domain_lookup)
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -565,7 +670,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
u'You cannot disable the user you are currently '
|
||||
u'logged in as.')
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list',)})
|
||||
@test.create_stubs({api.keystone: ('user_list', 'domain_lookup')})
|
||||
def test_delete_user_with_improper_permissions(self):
|
||||
domain = self._get_default_domain()
|
||||
domain_id = domain.id
|
||||
@ -573,6 +678,33 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
for i in range(0, 2):
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
formData = {'action': 'users__delete__%s' % self.request.user.id}
|
||||
res = self.client.post(USERS_INDEX_URL, formData, follow=True)
|
||||
|
||||
self.assertEqual(list(res.context['messages'])[0].message,
|
||||
u'You are not allowed to delete user: %s'
|
||||
% self.request.user.username)
|
||||
|
||||
@test.create_stubs({api.keystone: ('user_list', 'domain_lookup')})
|
||||
def test_delete_user_with_improper_permissions_domain_name(self):
|
||||
domain = self._get_default_domain()
|
||||
domains = self.domains.list()
|
||||
domain_id = domain.id
|
||||
users = self._get_users(domain_id)
|
||||
domain_lookup = dict((d.id, d.name) for d in domains)
|
||||
|
||||
for u in users:
|
||||
u.domain_name = domain_lookup.get(u.domain_id)
|
||||
|
||||
for i in range(0, 2):
|
||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||
.AndReturn(users)
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn(domain_lookup)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -662,10 +794,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
||||
admin=True).AndReturn(user)
|
||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||
domain_id).AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(),
|
||||
domain=domain_id,
|
||||
user=user.id) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=domain.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=user.id).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.user_update(IsA(http.HttpRequest),
|
||||
user.id,
|
||||
email=user.email,
|
||||
@ -696,17 +834,27 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
'tenant_list',
|
||||
'get_default_role',
|
||||
'role_list',
|
||||
'user_list')})
|
||||
'user_list',
|
||||
'domain_lookup')})
|
||||
def test_modal_create_user_with_passwords_not_matching(self):
|
||||
domain = self._get_default_domain()
|
||||
|
||||
api.keystone.get_default_domain(IgnoreArg()) \
|
||||
.AndReturn(domain)
|
||||
api.keystone.tenant_list(IgnoreArg(), domain=None, user=None) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
.MultipleTimes().AndReturn(domain)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), domain=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
else:
|
||||
api.keystone.tenant_list(
|
||||
IgnoreArg(), user=None).AndReturn(
|
||||
[self.tenants.list(), False])
|
||||
|
||||
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||
api.keystone.user_list(IgnoreArg(), domain=None) \
|
||||
.AndReturn(self.users.list())
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||
api.keystone.get_default_role(IgnoreArg()) \
|
||||
.AndReturn(self.roles.first())
|
||||
self.mox.ReplayAll()
|
||||
@ -726,6 +874,10 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
||||
self.selenium.find_element_by_id("id_password").send_keys("test")
|
||||
self.selenium.find_element_by_id("id_confirm_password").send_keys("te")
|
||||
self.selenium.find_element_by_id("id_email").send_keys("a@b.com")
|
||||
|
||||
wait.until(lambda x: self.selenium.find_element_by_id(
|
||||
"id_confirm_password_error"))
|
||||
|
||||
self.assertTrue(self._is_element_present("id_confirm_password_error"),
|
||||
"Couldn't find password error element.")
|
||||
|
||||
|
@ -50,9 +50,10 @@ class IndexView(tables.DataTableView):
|
||||
|
||||
def get_data(self):
|
||||
users = []
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
|
||||
if policy.check((("identity", "identity:list_users"),),
|
||||
self.request):
|
||||
domain_context = api.keystone.get_effective_domain_id(self.request)
|
||||
try:
|
||||
users = api.keystone.user_list(self.request,
|
||||
domain=domain_context)
|
||||
@ -71,6 +72,11 @@ class IndexView(tables.DataTableView):
|
||||
else:
|
||||
msg = _("Insufficient privilege level to view user information.")
|
||||
messages.info(self.request, msg)
|
||||
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
domain_lookup = api.keystone.domain_lookup(self.request)
|
||||
for u in users:
|
||||
u.domain_name = domain_lookup.get(u.domain_id)
|
||||
return users
|
||||
|
||||
|
||||
@ -108,12 +114,18 @@ class UpdateView(forms.ModalFormView):
|
||||
user = self.get_object()
|
||||
domain_id = getattr(user, "domain_id", None)
|
||||
domain_name = ''
|
||||
# Retrieve the domain name where the project belong
|
||||
# Retrieve the domain name where the project belongs
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
try:
|
||||
domain = api.keystone.domain_get(self.request,
|
||||
domain_id)
|
||||
domain_name = domain.name
|
||||
if policy.check((("identity", "identity:get_domain"),),
|
||||
self.request):
|
||||
domain = api.keystone.domain_get(self.request, domain_id)
|
||||
domain_name = domain.name
|
||||
|
||||
else:
|
||||
domain = api.keystone.get_default_domain(self.request)
|
||||
domain_name = domain.get('name')
|
||||
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project domain.'))
|
||||
@ -176,8 +188,14 @@ class DetailView(views.HorizonTemplateView):
|
||||
domain_name = ''
|
||||
if api.keystone.VERSIONS.active >= 3:
|
||||
try:
|
||||
domain = api.keystone.domain_get(self.request, domain_id)
|
||||
domain_name = domain.name
|
||||
if policy.check((("identity", "identity:get_domain"),),
|
||||
self.request):
|
||||
domain = api.keystone.domain_get(
|
||||
self.request, domain_id)
|
||||
domain_name = domain.name
|
||||
else:
|
||||
domain = api.keystone.get_default_domain(self.request)
|
||||
domain_name = domain.get('name')
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project domain.'))
|
||||
|
@ -21,4 +21,9 @@ class Project(horizon.Dashboard):
|
||||
name = _("Project")
|
||||
slug = "project"
|
||||
|
||||
def can_access(self, context):
|
||||
request = context['request']
|
||||
has_project = request.user.token.project.get('id') is not None
|
||||
return super(Project, self).can_access(context) and has_project
|
||||
|
||||
horizon.register(Project)
|
||||
|
@ -66,7 +66,11 @@ WEBROOT = '/'
|
||||
|
||||
# Overrides the default domain used when running on single-domain model
|
||||
# with Keystone V3. All entities will be created in the default domain.
|
||||
#OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default'
|
||||
# NOTE: This value must be the ID of the default domain, NOT the name.
|
||||
# Also, you will most likely have a value in the keystone policy file like this
|
||||
# "cloud_admin": "rule:admin_required and domain_id:<your domain id>"
|
||||
# This value must match the domain id specified there.
|
||||
#OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'default'
|
||||
|
||||
# Set this to True to enable panels that provide the ability for users to
|
||||
# manage Identity Providers (IdPs) and establish a set of rules to map
|
||||
|
@ -40,7 +40,10 @@ class PolicyTargetMixin(object):
|
||||
|
||||
policy_target_attrs = (("project_id", "tenant_id"),
|
||||
("user_id", "user_id"),
|
||||
("domain_id", "domain_id"))
|
||||
("domain_id", "domain_id"),
|
||||
("target.project.domain_id", "domain_id"),
|
||||
("target.user.domain_id", "domain_id"),
|
||||
("target.group.domain_id", "domain_id"))
|
||||
|
||||
def get_policy_target(self, request, datum=None):
|
||||
policy_target = {}
|
||||
|
@ -36,6 +36,11 @@ def get_user_home(user):
|
||||
if dashboard is None:
|
||||
dashboard = horizon.get_default_dashboard()
|
||||
|
||||
# Domain Admin, Project Admin will default to identity
|
||||
if (user.token.project.get('id') is None or
|
||||
(user.is_superuser and user.token.project.get('id'))):
|
||||
dashboard = horizon.get_dashboard('identity')
|
||||
|
||||
return dashboard.get_absolute_url()
|
||||
|
||||
|
||||
|
22
releasenotes/notes/domains-0581aa42773d5f41.yaml
Normal file
22
releasenotes/notes/domains-0581aa42773d5f41.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
features:
|
||||
- Added support for managing domains and projects when using Keystone v3.
|
||||
Horizon now maintains a domain scoped token for users who have a role on a
|
||||
domain, a project scoped token for users who have a role on a project, or
|
||||
both a domain scoped token and project scoped token for users who have
|
||||
roles on both.
|
||||
- |
|
||||
Domain management supports the following use cases:
|
||||
|
||||
* Cloud Admin - View and manage identity resources across domains
|
||||
* Domain Admin - View and manage identity resources in the domain logged in
|
||||
* User - View identity project in the domain logged in
|
||||
|
||||
other:
|
||||
- |
|
||||
Current limitations on managing identity resources with Keystone v3:
|
||||
|
||||
* Does not support role assignments across domains, such as giving a user
|
||||
in domain1 access to domain2.
|
||||
* Does not support project admins managing Keystone projects.
|
||||
* Does not support hierarchical project management.
|
Loading…
Reference in New Issue
Block a user