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__)
|
LOG = logging.getLogger(__name__)
|
||||||
DEFAULT_ROLE = None
|
DEFAULT_ROLE = None
|
||||||
|
DEFAULT_DOMAIN = getattr(settings, 'OPENSTACK_KEYSTONE_DEFAULT_DOMAIN',
|
||||||
|
'default')
|
||||||
|
|
||||||
|
|
||||||
# Set up our data structure for managing Identity API versions, and
|
# 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
|
The client is cached so that subsequent API calls during the same
|
||||||
request/response cycle don't have to be re-authenticated.
|
request/response cycle don't have to be re-authenticated.
|
||||||
"""
|
"""
|
||||||
|
api_version = VERSIONS.get_active_version()
|
||||||
user = request.user
|
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 admin:
|
||||||
if not policy.check((("identity", "admin_required"),), request):
|
if not policy.check((("identity", "admin_required"),), request):
|
||||||
raise exceptions.NotAuthorized
|
raise exceptions.NotAuthorized
|
||||||
@ -154,8 +166,6 @@ def keystoneclient(request, admin=False):
|
|||||||
'OPENSTACK_ENDPOINT_TYPE',
|
'OPENSTACK_ENDPOINT_TYPE',
|
||||||
'internalURL')
|
'internalURL')
|
||||||
|
|
||||||
api_version = VERSIONS.get_active_version()
|
|
||||||
|
|
||||||
# Take care of client connection caching/fetching a new client.
|
# Take care of client connection caching/fetching a new client.
|
||||||
# Admin vs. non-admin clients are cached separately for token matching.
|
# Admin vs. non-admin clients are cached separately for token matching.
|
||||||
cache_attr = "_keystoneclient_admin" if admin \
|
cache_attr = "_keystoneclient_admin" if admin \
|
||||||
@ -170,7 +180,7 @@ def keystoneclient(request, admin=False):
|
|||||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||||
LOG.debug("Creating a new keystoneclient connection to %s." % endpoint)
|
LOG.debug("Creating a new keystoneclient connection to %s." % endpoint)
|
||||||
remote_addr = request.environ.get('REMOTE_ADDR', '')
|
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,
|
endpoint=endpoint,
|
||||||
original_ip=remote_addr,
|
original_ip=remote_addr,
|
||||||
insecure=insecure,
|
insecure=insecure,
|
||||||
@ -183,7 +193,7 @@ def keystoneclient(request, admin=False):
|
|||||||
|
|
||||||
def domain_create(request, name, description=None, enabled=None):
|
def domain_create(request, name, description=None, enabled=None):
|
||||||
manager = keystoneclient(request, admin=True).domains
|
manager = keystoneclient(request, admin=True).domains
|
||||||
return manager.create(name,
|
return manager.create(name=name,
|
||||||
description=description,
|
description=description,
|
||||||
enabled=enabled)
|
enabled=enabled)
|
||||||
|
|
||||||
@ -203,10 +213,29 @@ def domain_list(request):
|
|||||||
return manager.list()
|
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,
|
def domain_update(request, domain_id, name=None, description=None,
|
||||||
enabled=None):
|
enabled=None):
|
||||||
manager = keystoneclient(request, admin=True).domains
|
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,
|
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()
|
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.
|
"""Gets the default domain object to use when creating Identity object.
|
||||||
|
|
||||||
Returns the domain context if is set, otherwise return the domain
|
Returns the domain context if is set, otherwise return the domain
|
||||||
of the logon user.
|
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_id = request.session.get("domain_context", None)
|
||||||
domain_name = request.session.get("domain_context_name", None)
|
domain_name = request.session.get("domain_context_name", None)
|
||||||
# if running in Keystone V3 or later
|
# if running in Keystone V3 or later
|
||||||
if VERSIONS.active >= 3 and not domain_id:
|
if VERSIONS.active >= 3 and domain_id is None:
|
||||||
# if no domain context set, default to users' domain
|
# if no domain context set, default to user's domain
|
||||||
domain_id = request.user.user_domain_id
|
domain_id = request.user.user_domain_id
|
||||||
try:
|
domain_name = request.user.user_domain_name
|
||||||
domain = domain_get(request, domain_id)
|
if get_name:
|
||||||
domain_name = domain.name
|
try:
|
||||||
except Exception:
|
domain = domain_get(request, domain_id)
|
||||||
LOG.warning("Unable to retrieve Domain: %s" % domain_id)
|
domain_name = domain.name
|
||||||
|
except Exception:
|
||||||
|
LOG.warning("Unable to retrieve Domain: %s" % domain_id)
|
||||||
domain = base.APIDictWrapper({"id": domain_id,
|
domain = base.APIDictWrapper({"id": domain_id,
|
||||||
"name": domain_name})
|
"name": domain_name})
|
||||||
return domain
|
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?
|
# 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
|
# 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
|
# 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:
|
if paginate and len(tenants) > page_size:
|
||||||
tenants.pop(-1)
|
tenants.pop(-1)
|
||||||
has_more_data = True
|
has_more_data = True
|
||||||
|
# V3 API
|
||||||
else:
|
else:
|
||||||
|
domain_id = get_effective_domain_id(request)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"domain": domain,
|
"domain": domain_id,
|
||||||
"user": user
|
"user": user
|
||||||
}
|
}
|
||||||
if filters is not None:
|
if filters is not None:
|
||||||
kwargs.update(filters)
|
kwargs.update(filters)
|
||||||
tenants = manager.list(**kwargs)
|
tenants = manager.list(**kwargs)
|
||||||
return (tenants, has_more_data)
|
return tenants, has_more_data
|
||||||
|
|
||||||
|
|
||||||
def tenant_update(request, project, name=None, description=None,
|
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)
|
groups_roles = collections.defaultdict(list)
|
||||||
project_role_assignments = role_assignments_list(request,
|
project_role_assignments = role_assignments_list(request,
|
||||||
project=project)
|
project=project)
|
||||||
|
|
||||||
for role_assignment in project_role_assignments:
|
for role_assignment in project_role_assignments:
|
||||||
if not hasattr(role_assignment, 'group'):
|
if not hasattr(role_assignment, 'group'):
|
||||||
continue
|
continue
|
||||||
group_id = role_assignment.group['id']
|
group_id = role_assignment.group['id']
|
||||||
role_id = role_assignment.role['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
|
return groups_roles
|
||||||
|
|
||||||
|
|
||||||
def role_assignments_list(request, project=None, user=None, role=None,
|
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:
|
if VERSIONS.active < 3:
|
||||||
raise exceptions.NotAvailable
|
raise exceptions.NotAvailable
|
||||||
|
|
||||||
|
if include_subtree:
|
||||||
|
domain = None
|
||||||
|
|
||||||
manager = keystoneclient(request, admin=True).role_assignments
|
manager = keystoneclient(request, admin=True).role_assignments
|
||||||
|
|
||||||
return manager.list(project=project, user=user, role=role, group=group,
|
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):
|
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):
|
def get_domain_users_roles(request, domain):
|
||||||
users_roles = collections.defaultdict(list)
|
users_roles = collections.defaultdict(list)
|
||||||
domain_role_assignments = role_assignments_list(request,
|
domain_role_assignments = role_assignments_list(request,
|
||||||
domain=domain)
|
domain=domain,
|
||||||
|
include_subtree=False)
|
||||||
for role_assignment in domain_role_assignments:
|
for role_assignment in domain_role_assignments:
|
||||||
if not hasattr(role_assignment, 'user'):
|
if not hasattr(role_assignment, 'user'):
|
||||||
continue
|
continue
|
||||||
user_id = role_assignment.user['id']
|
user_id = role_assignment.user['id']
|
||||||
role_id = role_assignment.role['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
|
return users_roles
|
||||||
|
|
||||||
|
|
||||||
@ -609,7 +680,11 @@ def get_project_users_roles(request, project):
|
|||||||
continue
|
continue
|
||||||
user_id = role_assignment.user['id']
|
user_id = role_assignment.user['id']
|
||||||
role_id = role_assignment.role['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
|
return users_roles
|
||||||
|
|
||||||
|
|
||||||
@ -759,6 +834,11 @@ def get_version():
|
|||||||
return VERSIONS.active
|
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():
|
def is_federation_management_enabled():
|
||||||
return getattr(settings, 'OPENSTACK_KEYSTONE_FEDERATION_MANAGEMENT', False)
|
return getattr(settings, 'OPENSTACK_KEYSTONE_FEDERATION_MANAGEMENT', False)
|
||||||
|
|
||||||
|
@ -21,6 +21,6 @@ class Admin(horizon.Dashboard):
|
|||||||
name = _("Admin")
|
name = _("Admin")
|
||||||
slug = "admin"
|
slug = "admin"
|
||||||
permissions = ('openstack.roles.admin',)
|
permissions = ('openstack.roles.admin',)
|
||||||
|
policy_rules = (("identity", "cloud_admin"),)
|
||||||
|
|
||||||
horizon.register(Admin)
|
horizon.register(Admin)
|
||||||
|
@ -28,3 +28,11 @@ class Domains(horizon.Panel):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def can_register():
|
def can_register():
|
||||||
return keystone.VERSIONS.active >= 3
|
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")
|
verbose_name = _("Manage Members")
|
||||||
url = "horizon:identity:domains:update"
|
url = "horizon:identity:domains:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
policy_rules = (("identity", "identity:list_users"),
|
policy_rules = (("identity", "identity:update_domain"),)
|
||||||
("identity", "identity:list_roles"),
|
|
||||||
("identity", "identity:list_role_assignments"))
|
|
||||||
|
|
||||||
def get_link_url(self, domain):
|
def get_link_url(self, domain):
|
||||||
step = 'update_user_members'
|
step = 'update_user_members'
|
||||||
@ -56,6 +54,7 @@ class UpdateGroupsLink(tables.LinkAction):
|
|||||||
url = "horizon:identity:domains:update"
|
url = "horizon:identity:domains:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
|
policy_rules = (("identity", "identity:update_domain"),)
|
||||||
|
|
||||||
def get_link_url(self, domain):
|
def get_link_url(self, domain):
|
||||||
step = 'update_group_members'
|
step = 'update_group_members'
|
||||||
@ -227,7 +226,7 @@ class SetDomainContext(tables.Action):
|
|||||||
verbose_name = _("Set Domain Context")
|
verbose_name = _("Set Domain Context")
|
||||||
url = constants.DOMAINS_INDEX_URL
|
url = constants.DOMAINS_INDEX_URL
|
||||||
preempt = True
|
preempt = True
|
||||||
policy_rules = (('identity', 'admin_required'),)
|
policy_rules = (('identity', 'identity:update_domain'),)
|
||||||
|
|
||||||
def allowed(self, request, datum):
|
def allowed(self, request, datum):
|
||||||
multidomain_support = getattr(settings,
|
multidomain_support = getattr(settings,
|
||||||
@ -262,7 +261,7 @@ class UnsetDomainContext(tables.Action):
|
|||||||
url = constants.DOMAINS_INDEX_URL
|
url = constants.DOMAINS_INDEX_URL
|
||||||
preempt = True
|
preempt = True
|
||||||
requires_input = False
|
requires_input = False
|
||||||
policy_rules = (('identity', 'admin_required'),)
|
policy_rules = (('identity', 'identity:update_domain'),)
|
||||||
|
|
||||||
def allowed(self, request, datum):
|
def allowed(self, request, datum):
|
||||||
ctx = request.session.get("domain_context", None)
|
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) \
|
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(users)
|
.AndReturn(users)
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id) \
|
domain=domain.id,
|
||||||
|
include_subtree=False) \
|
||||||
.AndReturn(role_assignments)
|
.AndReturn(role_assignments)
|
||||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(groups)
|
.AndReturn(groups)
|
||||||
@ -331,7 +332,8 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(users)
|
.AndReturn(users)
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id) \
|
domain=domain.id,
|
||||||
|
include_subtree=False) \
|
||||||
.AndReturn(role_assignments)
|
.AndReturn(role_assignments)
|
||||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(groups)
|
.AndReturn(groups)
|
||||||
@ -354,15 +356,19 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# handle
|
# handle
|
||||||
api.keystone.domain_update(IsA(http.HttpRequest),
|
api.keystone.domain_update(IsA(http.HttpRequest),
|
||||||
|
domain.id,
|
||||||
|
name=domain.name,
|
||||||
description=test_description,
|
description=test_description,
|
||||||
domain_id=domain.id,
|
enabled=domain.enabled).AndReturn(None)
|
||||||
enabled=domain.enabled,
|
|
||||||
name=domain.name).AndReturn(None)
|
|
||||||
|
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id) \
|
domain=domain.id,
|
||||||
|
include_subtree=False) \
|
||||||
.AndReturn(role_assignments)
|
.AndReturn(role_assignments)
|
||||||
|
|
||||||
|
api.keystone.user_list(IsA(http.HttpRequest),
|
||||||
|
domain=domain.id).AndReturn(users)
|
||||||
|
|
||||||
# Give user 3 role 1
|
# Give user 3 role 1
|
||||||
api.keystone.add_domain_user_role(IsA(http.HttpRequest),
|
api.keystone.add_domain_user_role(IsA(http.HttpRequest),
|
||||||
domain=domain.id,
|
domain=domain.id,
|
||||||
@ -468,7 +474,8 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
api.keystone.user_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(users)
|
.AndReturn(users)
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id) \
|
domain=domain.id,
|
||||||
|
include_subtree=False) \
|
||||||
.AndReturn(role_assignments)
|
.AndReturn(role_assignments)
|
||||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
api.keystone.group_list(IsA(http.HttpRequest), domain=domain.id) \
|
||||||
.AndReturn(groups)
|
.AndReturn(groups)
|
||||||
@ -492,10 +499,10 @@ class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# handle
|
# handle
|
||||||
api.keystone.domain_update(IsA(http.HttpRequest),
|
api.keystone.domain_update(IsA(http.HttpRequest),
|
||||||
|
domain.id,
|
||||||
|
name=domain.name,
|
||||||
description=test_description,
|
description=test_description,
|
||||||
domain_id=domain.id,
|
enabled=domain.enabled) \
|
||||||
enabled=domain.enabled,
|
|
||||||
name=domain.name) \
|
|
||||||
.AndRaise(self.exceptions.keystone)
|
.AndRaise(self.exceptions.keystone)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
@ -37,13 +37,13 @@ class IndexView(tables.DataTableView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
domains = []
|
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"),),
|
if policy.check((("identity", "identity:list_domains"),),
|
||||||
self.request):
|
self.request):
|
||||||
try:
|
try:
|
||||||
if domain_context:
|
if domain_id:
|
||||||
domain = api.keystone.domain_get(self.request,
|
domain = api.keystone.domain_get(self.request, domain_id)
|
||||||
domain_context)
|
|
||||||
domains.append(domain)
|
domains.append(domain)
|
||||||
else:
|
else:
|
||||||
domains = api.keystone.domain_list(self.request)
|
domains = api.keystone.domain_list(self.request)
|
||||||
@ -53,8 +53,7 @@ class IndexView(tables.DataTableView):
|
|||||||
elif policy.check((("identity", "identity:get_domain"),),
|
elif policy.check((("identity", "identity:get_domain"),),
|
||||||
self.request):
|
self.request):
|
||||||
try:
|
try:
|
||||||
domain = api.keystone.domain_get(self.request,
|
domain = api.keystone.domain_get(self.request, domain_id)
|
||||||
self.request.user.domain_id)
|
|
||||||
domains.append(domain)
|
domains.append(domain)
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
|
@ -322,8 +322,16 @@ class UpdateDomain(workflows.Workflow, IdentityMixIn):
|
|||||||
users_roles = api.keystone.get_domain_users_roles(request,
|
users_roles = api.keystone.get_domain_users_roles(request,
|
||||||
domain=domain_id)
|
domain=domain_id)
|
||||||
users_to_modify = len(users_roles)
|
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():
|
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
|
# Check if there have been any changes in the roles of
|
||||||
# Existing domain members.
|
# Existing domain members.
|
||||||
current_role_ids = list(users_roles[user_id])
|
current_role_ids = list(users_roles[user_id])
|
||||||
@ -484,7 +492,7 @@ class UpdateDomain(workflows.Workflow, IdentityMixIn):
|
|||||||
try:
|
try:
|
||||||
LOG.info('Updating domain with name "%s"' % data['name'])
|
LOG.info('Updating domain with name "%s"' % data['name'])
|
||||||
api.keystone.domain_update(request,
|
api.keystone.domain_update(request,
|
||||||
domain_id=domain_id,
|
domain_id,
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
description=data['description'],
|
description=data['description'],
|
||||||
enabled=data['enabled'])
|
enabled=data['enabled'])
|
||||||
|
@ -36,7 +36,7 @@ class CreateGroupForm(forms.SelfHandlingForm):
|
|||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.info('Creating group with name "%s"' % data['name'])
|
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(
|
api.keystone.group_create(
|
||||||
request,
|
request,
|
||||||
domain_id=domain_context,
|
domain_id=domain_context,
|
||||||
|
@ -27,3 +27,9 @@ class Groups(horizon.Panel):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def can_register():
|
def can_register():
|
||||||
return keystone.VERSIONS.active >= 3
|
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 import api
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.identity.groups import constants
|
from openstack_dashboard.dashboards.identity.groups import constants
|
||||||
|
from openstack_dashboard import policy
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -46,7 +47,7 @@ class CreateGroupLink(tables.LinkAction):
|
|||||||
return api.keystone.keystone_can_edit_group()
|
return api.keystone.keystone_can_edit_group()
|
||||||
|
|
||||||
|
|
||||||
class EditGroupLink(tables.LinkAction):
|
class EditGroupLink(policy.PolicyTargetMixin, tables.LinkAction):
|
||||||
name = "edit"
|
name = "edit"
|
||||||
verbose_name = _("Edit Group")
|
verbose_name = _("Edit Group")
|
||||||
url = constants.GROUPS_UPDATE_URL
|
url = constants.GROUPS_UPDATE_URL
|
||||||
@ -58,7 +59,7 @@ class EditGroupLink(tables.LinkAction):
|
|||||||
return api.keystone.keystone_can_edit_group()
|
return api.keystone.keystone_can_edit_group()
|
||||||
|
|
||||||
|
|
||||||
class DeleteGroupsAction(tables.DeleteAction):
|
class DeleteGroupsAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def action_present(count):
|
def action_present(count):
|
||||||
return ungettext_lazy(
|
return ungettext_lazy(
|
||||||
|
@ -65,11 +65,33 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
|||||||
self.assertContains(res, 'Edit')
|
self.assertContains(res, 'Edit')
|
||||||
self.assertContains(res, 'Delete Group')
|
self.assertContains(res, 'Delete Group')
|
||||||
|
|
||||||
|
@test.create_stubs({api.keystone: ('group_list',
|
||||||
|
'get_effective_domain_id')})
|
||||||
def test_index_with_domain(self):
|
def test_index_with_domain(self):
|
||||||
domain = self.domains.get(id="1")
|
domain = self.domains.get(id="1")
|
||||||
|
|
||||||
self.setSessionValues(domain_context=domain.id,
|
self.setSessionValues(domain_context=domain.id,
|
||||||
domain_context_name=domain.name)
|
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',
|
@test.create_stubs({api.keystone: ('group_list',
|
||||||
'keystone_can_edit_group')})
|
'keystone_can_edit_group')})
|
||||||
@ -159,17 +181,27 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.assertRedirectsNoFollow(res, GROUPS_INDEX_URL)
|
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',)})
|
'user_list',)})
|
||||||
def test_manage(self):
|
def test_manage(self):
|
||||||
group = self.groups.get(id="1")
|
group = self.groups.get(id="1")
|
||||||
group_members = self.users.list()
|
group_members = self.users.list()
|
||||||
|
domain_id = self._get_domain_id()
|
||||||
|
|
||||||
api.keystone.group_get(IsA(http.HttpRequest), group.id).\
|
api.keystone.group_get(IsA(http.HttpRequest), group.id).\
|
||||||
AndReturn(group)
|
AndReturn(group)
|
||||||
api.keystone.user_list(IgnoreArg(),
|
|
||||||
group=group.id).\
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
AndReturn(group_members)
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(GROUP_MANAGE_URL)
|
res = self.client.get(GROUP_MANAGE_URL)
|
||||||
@ -177,15 +209,25 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
|||||||
self.assertTemplateUsed(res, constants.GROUPS_MANAGE_VIEW_TEMPLATE)
|
self.assertTemplateUsed(res, constants.GROUPS_MANAGE_VIEW_TEMPLATE)
|
||||||
self.assertItemsEqual(res.context['table'].data, group_members)
|
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')})
|
'remove_group_user')})
|
||||||
def test_remove_user(self):
|
def test_remove_user(self):
|
||||||
group = self.groups.get(id="1")
|
group = self.groups.get(id="1")
|
||||||
user = self.users.get(id="2")
|
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(),
|
api.keystone.remove_group_user(IgnoreArg(),
|
||||||
group_id=group.id,
|
group_id=group.id,
|
||||||
user_id=user.id)
|
user_id=user.id)
|
||||||
@ -197,20 +239,24 @@ class GroupsViewTests(test.BaseAdminViewTests):
|
|||||||
self.assertRedirectsNoFollow(res, GROUP_MANAGE_URL)
|
self.assertRedirectsNoFollow(res, GROUP_MANAGE_URL)
|
||||||
self.assertMessageCount(success=1)
|
self.assertMessageCount(success=1)
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('group_get',
|
@test.create_stubs({api.keystone: ('get_effective_domain_id',
|
||||||
|
'group_get',
|
||||||
'user_list',
|
'user_list',
|
||||||
'add_group_user')})
|
'add_group_user')})
|
||||||
def test_add_user(self):
|
def test_add_user(self):
|
||||||
group = self.groups.get(id="1")
|
group = self.groups.get(id="1")
|
||||||
user = self.users.get(id="2")
|
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).\
|
api.keystone.group_get(IsA(http.HttpRequest), group.id).\
|
||||||
AndReturn(group)
|
AndReturn(group)
|
||||||
api.keystone.user_list(IgnoreArg(),
|
|
||||||
domain=group.domain_id).\
|
api.keystone.user_list(IgnoreArg(), domain=domain_id).\
|
||||||
AndReturn(self.users.list())
|
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:])
|
AndReturn(self.users.list()[2:])
|
||||||
|
|
||||||
api.keystone.add_group_user(IgnoreArg(),
|
api.keystone.add_group_user(IgnoreArg(),
|
||||||
|
@ -39,12 +39,13 @@ class IndexView(tables.DataTableView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
groups = []
|
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"),),
|
if policy.check((("identity", "identity:list_groups"),),
|
||||||
self.request):
|
self.request):
|
||||||
try:
|
try:
|
||||||
groups = api.keystone.group_list(self.request,
|
groups = api.keystone.group_list(self.request,
|
||||||
domain=domain_context)
|
domain=domain_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve group list.'))
|
_('Unable to retrieve group list.'))
|
||||||
@ -108,7 +109,9 @@ class GroupManageMixin(object):
|
|||||||
@memoized.memoized_method
|
@memoized.memoized_method
|
||||||
def _get_group_members(self):
|
def _get_group_members(self):
|
||||||
group_id = self.kwargs['group_id']
|
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
|
@memoized.memoized_method
|
||||||
def _get_group_non_members(self):
|
def _get_group_non_members(self):
|
||||||
|
@ -62,6 +62,14 @@ class UpdateMembersLink(tables.LinkAction):
|
|||||||
param = urlencode({"step": step})
|
param = urlencode({"step": step})
|
||||||
return "?".join([base_url, param])
|
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):
|
class UpdateGroupsLink(tables.LinkAction):
|
||||||
name = "groups"
|
name = "groups"
|
||||||
@ -72,7 +80,12 @@ class UpdateGroupsLink(tables.LinkAction):
|
|||||||
policy_rules = (("identity", "identity:list_groups"),)
|
policy_rules = (("identity", "identity:list_groups"),)
|
||||||
|
|
||||||
def allowed(self, request, project):
|
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):
|
def get_link_url(self, project):
|
||||||
step = 'update_group_members'
|
step = 'update_group_members'
|
||||||
@ -101,19 +114,30 @@ class CreateProject(tables.LinkAction):
|
|||||||
policy_rules = (('identity', 'identity:create_project'),)
|
policy_rules = (('identity', 'identity:create_project'),)
|
||||||
|
|
||||||
def allowed(self, request, 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"
|
name = "update"
|
||||||
verbose_name = _("Edit Project")
|
verbose_name = _("Edit Project")
|
||||||
url = "horizon:identity: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'),)
|
||||||
|
policy_target_attrs = (("target.project.domain_id", "domain_id"),)
|
||||||
|
|
||||||
def allowed(self, request, 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 ModifyQuotas(tables.LinkAction):
|
class ModifyQuotas(tables.LinkAction):
|
||||||
@ -124,6 +148,12 @@ class ModifyQuotas(tables.LinkAction):
|
|||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (('compute', "compute_extension:quotas:update"),)
|
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):
|
def get_link_url(self, project):
|
||||||
step = 'update_quotas'
|
step = 'update_quotas'
|
||||||
base_url = reverse(self.url, args=[project.id])
|
base_url = reverse(self.url, args=[project.id])
|
||||||
@ -131,7 +161,7 @@ class ModifyQuotas(tables.LinkAction):
|
|||||||
return "?".join([base_url, param])
|
return "?".join([base_url, param])
|
||||||
|
|
||||||
|
|
||||||
class DeleteTenantsAction(tables.DeleteAction):
|
class DeleteTenantsAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def action_present(count):
|
def action_present(count):
|
||||||
return ungettext_lazy(
|
return ungettext_lazy(
|
||||||
@ -149,8 +179,12 @@ class DeleteTenantsAction(tables.DeleteAction):
|
|||||||
)
|
)
|
||||||
|
|
||||||
policy_rules = (("identity", "identity:delete_project"),)
|
policy_rules = (("identity", "identity:delete_project"),)
|
||||||
|
policy_target_attrs = ("target.project.domain_id", "domain_id"),
|
||||||
|
|
||||||
def allowed(self, request, project):
|
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()
|
return api.keystone.keystone_can_edit_project()
|
||||||
|
|
||||||
def delete(self, request, obj_id):
|
def delete(self, request, obj_id):
|
||||||
@ -238,6 +272,17 @@ class TenantsTable(tables.DataTable):
|
|||||||
required=False),
|
required=False),
|
||||||
update_action=UpdateCell)
|
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):
|
def get_project_detail_link(self, project):
|
||||||
# this method is an ugly monkey patch, needed because
|
# this method is an ugly monkey patch, needed because
|
||||||
# the column link method does not provide access to the request
|
# 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):
|
class TenantsViewTests(test.BaseAdminViewTests):
|
||||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
@test.create_stubs({api.keystone: ('tenant_list', 'domain_lookup')})
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
|
domain = self.domains.get(id="1")
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||||
domain=None,
|
domain=None,
|
||||||
paginate=True,
|
paginate=True,
|
||||||
marker=None) \
|
marker=None) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
self.assertTemplateUsed(res, 'identity/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',
|
||||||
|
'get_effective_domain_id',
|
||||||
|
'domain_lookup')})
|
||||||
def test_index_with_domain_context(self):
|
def test_index_with_domain_context(self):
|
||||||
domain = self.domains.get(id="1")
|
domain = self.domains.get(id="1")
|
||||||
|
|
||||||
self.setSessionValues(domain_context=domain.id,
|
self.setSessionValues(domain_context=domain.id,
|
||||||
domain_context_name=domain.name)
|
domain_context_name=domain.name)
|
||||||
|
|
||||||
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.get_effective_domain_id(IgnoreArg()).AndReturn(domain.id)
|
||||||
|
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||||
domain=domain.id,
|
domain=domain.id,
|
||||||
paginate=True,
|
paginate=True,
|
||||||
marker=None) \
|
marker=None) \
|
||||||
.AndReturn([domain_tenants, False])
|
.AndReturn([domain_tenants, False])
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
@ -86,14 +98,18 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
class ProjectsViewNonAdminTests(test.TestCase):
|
class ProjectsViewNonAdminTests(test.TestCase):
|
||||||
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check)
|
@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):
|
def test_index(self):
|
||||||
|
domain = self.domains.get(id="1")
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest),
|
api.keystone.tenant_list(IsA(http.HttpRequest),
|
||||||
user=self.user.id,
|
user=self.user.id,
|
||||||
paginate=True,
|
paginate=True,
|
||||||
marker=None,
|
marker=None,
|
||||||
admin=False) \
|
admin=False) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
@ -393,6 +409,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
'role_list',
|
'role_list',
|
||||||
'group_list',
|
'group_list',
|
||||||
'get_default_domain',
|
'get_default_domain',
|
||||||
|
'is_cloud_admin',
|
||||||
'get_default_role'),
|
'get_default_role'),
|
||||||
quotas: ('get_default_quota_data',
|
quotas: ('get_default_quota_data',
|
||||||
'get_disabled_quotas')})
|
'get_disabled_quotas')})
|
||||||
@ -407,8 +424,13 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
# init
|
# init
|
||||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(default_domain)
|
.AndReturn(default_domain)
|
||||||
|
|
||||||
|
api.keystone.is_cloud_admin(IsA(http.HttpRequest)) \
|
||||||
|
.MultipleTimes().AndReturn(True)
|
||||||
|
|
||||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.disabled_quotas.first())
|
.AndReturn(self.disabled_quotas.first())
|
||||||
|
|
||||||
quotas.get_default_quota_data(IsA(http.HttpRequest)) \
|
quotas.get_default_quota_data(IsA(http.HttpRequest)) \
|
||||||
.AndRaise(self.exceptions.nova)
|
.AndRaise(self.exceptions.nova)
|
||||||
|
|
||||||
@ -456,7 +478,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# init
|
# init
|
||||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(default_domain)
|
.MultipleTimes().AndReturn(default_domain)
|
||||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.disabled_quotas.first())
|
.AndReturn(self.disabled_quotas.first())
|
||||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||||
@ -515,7 +537,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# init
|
# init
|
||||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(default_domain)
|
.MultipleTimes().AndReturn(default_domain)
|
||||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.disabled_quotas.first())
|
.AndReturn(self.disabled_quotas.first())
|
||||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||||
@ -600,8 +622,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
roles = self.roles.list()
|
roles = self.roles.list()
|
||||||
|
|
||||||
# init
|
# init
|
||||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
api.keystone.get_default_domain(
|
||||||
.AndReturn(default_domain)
|
IsA(http.HttpRequest)).MultipleTimes().AndReturn(default_domain)
|
||||||
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.disabled_quotas.first())
|
.AndReturn(self.disabled_quotas.first())
|
||||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
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']
|
workflow_data[GROUP_ROLE_PREFIX + "2"] = ['1', '2', '3']
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
.AndReturn(role_assignments)
|
.MultipleTimes().AndReturn(role_assignments)
|
||||||
# Give user 1 role 2
|
# Give user 1 role 2
|
||||||
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
|
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id,
|
project=self.tenant.id,
|
||||||
@ -879,7 +901,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
if keystone_api_version >= 3:
|
if keystone_api_version >= 3:
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
.AndReturn(role_assignments)
|
.MultipleTimes().AndReturn(role_assignments)
|
||||||
else:
|
else:
|
||||||
api.keystone.user_list(IsA(http.HttpRequest),
|
api.keystone.user_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
@ -890,10 +912,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
user.id,
|
user.id,
|
||||||
self.tenant.id).AndReturn(roles)
|
self.tenant.id).AndReturn(roles)
|
||||||
|
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
|
||||||
project=self.tenant.id) \
|
|
||||||
.AndReturn(role_assignments)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:identity:projects:update',
|
url = reverse('horizon:identity:projects:update',
|
||||||
@ -922,6 +940,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_get',
|
@test.create_stubs({api.keystone: ('tenant_get',
|
||||||
'domain_get',
|
'domain_get',
|
||||||
|
'get_effective_domain_id',
|
||||||
'tenant_update',
|
'tenant_update',
|
||||||
'get_default_role',
|
'get_default_role',
|
||||||
'roles_for_user',
|
'roles_for_user',
|
||||||
@ -938,7 +957,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
api.cinder: ('tenant_quota_update',),
|
api.cinder: ('tenant_quota_update',),
|
||||||
quotas: ('get_tenant_quota_data',
|
quotas: ('get_tenant_quota_data',
|
||||||
'get_disabled_quotas',
|
'get_disabled_quotas',
|
||||||
'tenant_quota_usages')})
|
'tenant_quota_usages',)})
|
||||||
def test_update_project_save(self, neutron=False):
|
def test_update_project_save(self, neutron=False):
|
||||||
keystone_api_version = api.keystone.VERSIONS.active
|
keystone_api_version = api.keystone.VERSIONS.active
|
||||||
|
|
||||||
@ -979,11 +998,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data = {}
|
workflow_data = {}
|
||||||
|
|
||||||
if keystone_api_version >= 3:
|
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),
|
api.keystone.user_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
.AndReturn(proj_users)
|
.AndReturn(proj_users)
|
||||||
@ -993,10 +1008,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
user.id,
|
user.id,
|
||||||
self.tenant.id).AndReturn(roles)
|
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 + "1"] = ['3'] # admin role
|
||||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['2'] # member role
|
workflow_data[USER_ROLE_PREFIX + "2"] = ['2'] # member role
|
||||||
# Group assignment form data
|
# Group assignment form data
|
||||||
@ -1010,16 +1021,22 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
quota.metadata_items = 444
|
quota.metadata_items = 444
|
||||||
quota.volumes = 444
|
quota.volumes = 444
|
||||||
|
|
||||||
updated_project = {"name": project._info["name"],
|
|
||||||
"description": project._info["description"],
|
|
||||||
"enabled": project.enabled}
|
|
||||||
updated_quota = self._get_quota_info(quota)
|
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
|
# handle
|
||||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||||
project.id,
|
project.id,
|
||||||
**updated_project) \
|
name=project._info["name"],
|
||||||
.AndReturn(project)
|
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,
|
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||||
proj_users, roles, workflow_data)
|
proj_users, roles, workflow_data)
|
||||||
@ -1092,6 +1109,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_get',
|
@test.create_stubs({api.keystone: ('tenant_get',
|
||||||
'domain_get',
|
'domain_get',
|
||||||
|
'get_effective_domain_id',
|
||||||
'tenant_update',
|
'tenant_update',
|
||||||
'get_default_role',
|
'get_default_role',
|
||||||
'roles_for_user',
|
'roles_for_user',
|
||||||
@ -1148,7 +1166,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
if keystone_api_version >= 3:
|
if keystone_api_version >= 3:
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
.AndReturn(role_assignments)
|
.MultipleTimes().AndReturn(role_assignments)
|
||||||
else:
|
else:
|
||||||
api.keystone.user_list(IsA(http.HttpRequest),
|
api.keystone.user_list(IsA(http.HttpRequest),
|
||||||
project=self.tenant.id) \
|
project=self.tenant.id) \
|
||||||
@ -1164,10 +1182,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
workflow_data.setdefault(USER_ROLE_PREFIX + role_ids[0], []) \
|
workflow_data.setdefault(USER_ROLE_PREFIX + role_ids[0], []) \
|
||||||
.append(user.id)
|
.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]
|
role_ids = [role.id for role in roles]
|
||||||
for group in groups:
|
for group in groups:
|
||||||
if role_ids:
|
if role_ids:
|
||||||
@ -1181,17 +1195,21 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
quota.metadata_items = 444
|
quota.metadata_items = 444
|
||||||
quota.volumes = 444
|
quota.volumes = 444
|
||||||
|
|
||||||
updated_project = {"name": project._info["name"],
|
|
||||||
"description": project._info["description"],
|
|
||||||
"enabled": project.enabled}
|
|
||||||
updated_quota = self._get_quota_info(quota)
|
updated_quota = self._get_quota_info(quota)
|
||||||
|
|
||||||
# handle
|
# handle
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
||||||
.AndReturn(quota_usages)
|
.AndReturn(quota_usages)
|
||||||
|
|
||||||
|
api.keystone.get_effective_domain_id(
|
||||||
|
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||||
|
|
||||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||||
project.id,
|
project.id,
|
||||||
**updated_project) \
|
name=project._info["name"],
|
||||||
|
domain=domain_id,
|
||||||
|
description=project._info['description'],
|
||||||
|
enabled=project.enabled) \
|
||||||
.AndRaise(self.exceptions.keystone)
|
.AndRaise(self.exceptions.keystone)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -1213,6 +1231,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_get',
|
@test.create_stubs({api.keystone: ('tenant_get',
|
||||||
'domain_get',
|
'domain_get',
|
||||||
|
'get_effective_domain_id',
|
||||||
'tenant_update',
|
'tenant_update',
|
||||||
'get_default_role',
|
'get_default_role',
|
||||||
'roles_for_user',
|
'roles_for_user',
|
||||||
@ -1257,8 +1276,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
||||||
.MultipleTimes().AndReturn(default_role)
|
.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)) \
|
api.keystone.role_list(IsA(http.HttpRequest)) \
|
||||||
.MultipleTimes().AndReturn(roles)
|
.MultipleTimes().AndReturn(roles)
|
||||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||||
@ -1266,24 +1287,16 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data = {}
|
workflow_data = {}
|
||||||
|
|
||||||
if keystone_api_version >= 3:
|
if keystone_api_version < 3:
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.user_list(
|
||||||
project=self.tenant.id) \
|
IsA(http.HttpRequest),
|
||||||
.AndReturn(role_assignments)
|
project=self.tenant.id).AndReturn(proj_users)
|
||||||
else:
|
|
||||||
api.keystone.user_list(IsA(http.HttpRequest),
|
|
||||||
project=self.tenant.id) \
|
|
||||||
.AndReturn(proj_users)
|
|
||||||
|
|
||||||
for user in proj_users:
|
for user in proj_users:
|
||||||
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
||||||
user.id,
|
user.id,
|
||||||
self.tenant.id).AndReturn(roles)
|
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 + "1"] = ['1', '3'] # admin role
|
||||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['1', '2', '3'] # member role
|
workflow_data[USER_ROLE_PREFIX + "2"] = ['1', '2', '3'] # member role
|
||||||
# Group role assignment data
|
# Group role assignment data
|
||||||
@ -1297,16 +1310,21 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
quota[0].limit = 444
|
quota[0].limit = 444
|
||||||
quota[1].limit = -1
|
quota[1].limit = -1
|
||||||
|
|
||||||
updated_project = {"name": project._info["name"],
|
|
||||||
"description": project._info["description"],
|
|
||||||
"enabled": project.enabled}
|
|
||||||
updated_quota = self._get_quota_info(quota)
|
updated_quota = self._get_quota_info(quota)
|
||||||
|
|
||||||
# handle
|
# handle
|
||||||
|
api.keystone.get_effective_domain_id(
|
||||||
|
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||||
|
|
||||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||||
project.id,
|
project.id,
|
||||||
**updated_project) \
|
name=project._info["name"],
|
||||||
.AndReturn(project)
|
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,
|
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||||
proj_users, roles, workflow_data)
|
proj_users, roles, workflow_data)
|
||||||
@ -1352,7 +1370,8 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
'add_group_role',
|
'add_group_role',
|
||||||
'group_list',
|
'group_list',
|
||||||
'role_list',
|
'role_list',
|
||||||
'role_assignments_list'),
|
'role_assignments_list',
|
||||||
|
'get_effective_domain_id'),
|
||||||
quotas: ('get_tenant_quota_data',
|
quotas: ('get_tenant_quota_data',
|
||||||
'get_disabled_quotas',
|
'get_disabled_quotas',
|
||||||
'tenant_quota_usages')})
|
'tenant_quota_usages')})
|
||||||
@ -1384,8 +1403,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
api.keystone.get_default_role(IsA(http.HttpRequest)) \
|
||||||
.MultipleTimes().AndReturn(default_role)
|
.MultipleTimes().AndReturn(default_role)
|
||||||
|
|
||||||
api.keystone.user_list(IsA(http.HttpRequest), domain=domain_id) \
|
api.keystone.user_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||||
.AndReturn(users)
|
.AndReturn(users)
|
||||||
|
|
||||||
api.keystone.role_list(IsA(http.HttpRequest)) \
|
api.keystone.role_list(IsA(http.HttpRequest)) \
|
||||||
.MultipleTimes().AndReturn(roles)
|
.MultipleTimes().AndReturn(roles)
|
||||||
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
|
||||||
@ -1393,24 +1414,16 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
workflow_data = {}
|
workflow_data = {}
|
||||||
|
|
||||||
if keystone_api_version >= 3:
|
if keystone_api_version < 3:
|
||||||
api.keystone.role_assignments_list(IsA(http.HttpRequest),
|
api.keystone.user_list(
|
||||||
project=self.tenant.id) \
|
IsA(http.HttpRequest),
|
||||||
.AndReturn(role_assignments)
|
project=self.tenant.id).AndReturn(proj_users)
|
||||||
else:
|
|
||||||
api.keystone.user_list(IsA(http.HttpRequest),
|
|
||||||
project=self.tenant.id) \
|
|
||||||
.AndReturn(proj_users)
|
|
||||||
|
|
||||||
for user in proj_users:
|
for user in proj_users:
|
||||||
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
api.keystone.roles_for_user(IsA(http.HttpRequest),
|
||||||
user.id,
|
user.id,
|
||||||
self.tenant.id).AndReturn(roles)
|
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 + "1"] = ['1', '3'] # admin role
|
||||||
workflow_data[USER_ROLE_PREFIX + "2"] = ['1', '2', '3'] # member 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.metadata_items = 444
|
||||||
quota.volumes = 444
|
quota.volumes = 444
|
||||||
|
|
||||||
updated_project = {"name": project._info["name"],
|
|
||||||
"description": project._info["description"],
|
|
||||||
"enabled": project.enabled}
|
|
||||||
updated_quota = self._get_quota_info(quota)
|
updated_quota = self._get_quota_info(quota)
|
||||||
|
|
||||||
# handle
|
# handle
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
|
||||||
.AndReturn(quota_usages)
|
.AndReturn(quota_usages)
|
||||||
|
|
||||||
|
api.keystone.get_effective_domain_id(
|
||||||
|
IsA(http.HttpRequest)).MultipleTimes().AndReturn(domain_id)
|
||||||
|
|
||||||
api.keystone.tenant_update(IsA(http.HttpRequest),
|
api.keystone.tenant_update(IsA(http.HttpRequest),
|
||||||
project.id,
|
project.id,
|
||||||
**updated_project) \
|
name=project._info["name"],
|
||||||
.AndReturn(project)
|
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,
|
self._check_role_list(keystone_api_version, role_assignments, groups,
|
||||||
proj_users, roles, workflow_data)
|
proj_users, roles, workflow_data)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
# submit form data
|
# submit form data
|
||||||
@ -1592,7 +1612,8 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
|
|||||||
"The WITH_SELENIUM env variable is not set.")
|
"The WITH_SELENIUM env variable is not set.")
|
||||||
class SeleniumTests(test.SeleniumAdminTestCase):
|
class SeleniumTests(test.SeleniumAdminTestCase):
|
||||||
@test.create_stubs(
|
@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):
|
def test_inline_editing_update(self):
|
||||||
# Tenant List
|
# Tenant List
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
api.keystone.tenant_list(IgnoreArg(),
|
||||||
@ -1600,6 +1621,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
marker=None,
|
marker=None,
|
||||||
paginate=True) \
|
paginate=True) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||||
# Edit mod
|
# Edit mod
|
||||||
api.keystone.tenant_get(IgnoreArg(),
|
api.keystone.tenant_get(IgnoreArg(),
|
||||||
u'1',
|
u'1',
|
||||||
@ -1673,7 +1695,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
"'Changed test_tenant'")
|
"'Changed test_tenant'")
|
||||||
|
|
||||||
@test.create_stubs(
|
@test.create_stubs(
|
||||||
{api.keystone: ('tenant_list', 'tenant_get')})
|
{api.keystone: ('tenant_list', 'tenant_get', 'domain_lookup')})
|
||||||
def test_inline_editing_cancel(self):
|
def test_inline_editing_cancel(self):
|
||||||
# Tenant List
|
# Tenant List
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
api.keystone.tenant_list(IgnoreArg(),
|
||||||
@ -1681,6 +1703,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
marker=None,
|
marker=None,
|
||||||
paginate=True) \
|
paginate=True) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||||
# Edit mod
|
# Edit mod
|
||||||
api.keystone.tenant_get(IgnoreArg(),
|
api.keystone.tenant_get(IgnoreArg(),
|
||||||
u'1',
|
u'1',
|
||||||
|
@ -77,10 +77,12 @@ class IndexView(tables.DataTableView):
|
|||||||
tenants = []
|
tenants = []
|
||||||
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)
|
|
||||||
self._more = False
|
self._more = False
|
||||||
|
|
||||||
if policy.check((("identity", "identity:list_projects"),),
|
if policy.check((("identity", "identity:list_projects"),),
|
||||||
self.request):
|
self.request):
|
||||||
|
domain_context = api.keystone.get_effective_domain_id(self.request)
|
||||||
try:
|
try:
|
||||||
tenants, self._more = api.keystone.tenant_list(
|
tenants, self._more = api.keystone.tenant_list(
|
||||||
self.request,
|
self.request,
|
||||||
@ -106,6 +108,11 @@ class IndexView(tables.DataTableView):
|
|||||||
msg = \
|
msg = \
|
||||||
_("Insufficient privilege level to view project information.")
|
_("Insufficient privilege level to view project information.")
|
||||||
messages.info(self.request, msg)
|
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
|
return tenants
|
||||||
|
|
||||||
|
|
||||||
@ -126,6 +133,11 @@ class CreateProjectView(workflows.WorkflowView):
|
|||||||
workflow_class = project_workflows.CreateProject
|
workflow_class = project_workflows.CreateProject
|
||||||
|
|
||||||
def get_initial(self):
|
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()
|
initial = super(CreateProjectView, self).get_initial()
|
||||||
|
|
||||||
# Set the domain of the project
|
# Set the domain of the project
|
||||||
@ -133,33 +145,36 @@ class CreateProjectView(workflows.WorkflowView):
|
|||||||
initial["domain_id"] = domain.id
|
initial["domain_id"] = domain.id
|
||||||
initial["domain_name"] = domain.name
|
initial["domain_name"] = domain.name
|
||||||
|
|
||||||
|
# TODO(esp): fix this for Domain Admin or find a work around
|
||||||
# get initial quota defaults
|
# get initial quota defaults
|
||||||
try:
|
if api.keystone.is_cloud_admin(self.request):
|
||||||
quota_defaults = quotas.get_default_quota_data(self.request)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if api.base.is_service_enabled(self.request, 'network') and \
|
quota_defaults = quotas.get_default_quota_data(self.request)
|
||||||
api.neutron.is_quotas_extension_supported(
|
|
||||||
self.request):
|
try:
|
||||||
# TODO(jpichon): There is no API to access the Neutron
|
if api.base.is_service_enabled(
|
||||||
# default quotas (LP#1204956). For now, use the values
|
self.request, 'network') and \
|
||||||
# from the current project.
|
api.neutron.is_quotas_extension_supported(
|
||||||
project_id = self.request.user.project_id
|
self.request):
|
||||||
quota_defaults += api.neutron.tenant_quota_get(
|
# TODO(jpichon): There is no API to access the Neutron
|
||||||
self.request,
|
# default quotas (LP#1204956). For now, use the values
|
||||||
tenant_id=project_id)
|
# 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:
|
except Exception:
|
||||||
error_msg = _('Unable to retrieve default Neutron quota '
|
error_msg = _('Unable to retrieve default quota values.')
|
||||||
'values.')
|
|
||||||
self.add_error_to_step(error_msg, 'create_quotas')
|
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
|
return initial
|
||||||
|
|
||||||
|
|
||||||
@ -167,6 +182,11 @@ class UpdateProjectView(workflows.WorkflowView):
|
|||||||
workflow_class = project_workflows.UpdateProject
|
workflow_class = project_workflows.UpdateProject
|
||||||
|
|
||||||
def get_initial(self):
|
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()
|
initial = super(UpdateProjectView, self).get_initial()
|
||||||
|
|
||||||
project_id = self.kwargs['tenant_id']
|
project_id = self.kwargs['tenant_id']
|
||||||
@ -182,23 +202,32 @@ class UpdateProjectView(workflows.WorkflowView):
|
|||||||
# Retrieve the domain name where the project belong
|
# Retrieve the domain name where the project belong
|
||||||
if keystone.VERSIONS.active >= 3:
|
if keystone.VERSIONS.active >= 3:
|
||||||
try:
|
try:
|
||||||
domain = api.keystone.domain_get(self.request,
|
if policy.check((("identity", "identity:get_domain"),),
|
||||||
initial["domain_id"])
|
self.request):
|
||||||
initial["domain_name"] = domain.name
|
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:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve project domain.'),
|
_('Unable to retrieve project domain.'),
|
||||||
redirect=reverse(INDEX_URL))
|
redirect=reverse(INDEX_URL))
|
||||||
|
|
||||||
# get initial project quota
|
# get initial project quota
|
||||||
quota_data = quotas.get_tenant_quota_data(self.request,
|
if keystone.is_cloud_admin(self.request):
|
||||||
tenant_id=project_id)
|
quota_data = quotas.get_tenant_quota_data(self.request,
|
||||||
if api.base.is_service_enabled(self.request, 'network') and \
|
tenant_id=project_id)
|
||||||
api.neutron.is_quotas_extension_supported(self.request):
|
if api.base.is_service_enabled(self.request, 'network') and \
|
||||||
quota_data += api.neutron.tenant_quota_get(
|
api.neutron.is_quotas_extension_supported(
|
||||||
self.request, tenant_id=project_id)
|
self.request):
|
||||||
for field in quotas.QUOTA_FIELDS:
|
quota_data += api.neutron.tenant_quota_get(
|
||||||
initial[field] = quota_data.get(field).limit
|
self.request, tenant_id=project_id)
|
||||||
|
for field in quotas.QUOTA_FIELDS:
|
||||||
|
initial[field] = quota_data.get(field).limit
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve project details.'),
|
_('Unable to retrieve project details.'),
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
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.usage import quotas
|
||||||
from openstack_dashboard.utils.identity import IdentityMixIn
|
from openstack_dashboard.utils.identity import IdentityMixIn
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = "horizon:identity:projects:index"
|
INDEX_URL = "horizon:identity:projects:index"
|
||||||
ADD_USER_URL = "horizon:identity: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
|
||||||
@ -111,6 +115,7 @@ class UpdateProjectQuotaAction(ProjectQuotaAction):
|
|||||||
name = _("Quota")
|
name = _("Quota")
|
||||||
slug = 'update_quotas'
|
slug = 'update_quotas'
|
||||||
help_text = _("Set maximum quotas for the project.")
|
help_text = _("Set maximum quotas for the project.")
|
||||||
|
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||||
|
|
||||||
|
|
||||||
class CreateProjectQuotaAction(ProjectQuotaAction):
|
class CreateProjectQuotaAction(ProjectQuotaAction):
|
||||||
@ -118,6 +123,7 @@ class CreateProjectQuotaAction(ProjectQuotaAction):
|
|||||||
name = _("Quota")
|
name = _("Quota")
|
||||||
slug = 'create_quotas'
|
slug = 'create_quotas'
|
||||||
help_text = _("Set maximum quotas for the project.")
|
help_text = _("Set maximum quotas for the project.")
|
||||||
|
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||||
|
|
||||||
|
|
||||||
class UpdateProjectQuota(workflows.Step):
|
class UpdateProjectQuota(workflows.Step):
|
||||||
@ -186,13 +192,14 @@ class UpdateProjectMembersAction(workflows.MembershipAction):
|
|||||||
err_msg = _('Unable to retrieve user list. Please try again later.')
|
err_msg = _('Unable to retrieve user list. Please try again later.')
|
||||||
# Use the domain_id from the project
|
# Use the domain_id from the project
|
||||||
domain_id = self.initial.get("domain_id", None)
|
domain_id = self.initial.get("domain_id", None)
|
||||||
|
|
||||||
project_id = ''
|
project_id = ''
|
||||||
if 'project_id' in self.initial:
|
if 'project_id' in self.initial:
|
||||||
project_id = self.initial['project_id']
|
project_id = self.initial['project_id']
|
||||||
|
|
||||||
# Get the default role
|
# Get the default role
|
||||||
try:
|
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
|
# Default role is necessary to add members to a project
|
||||||
if default_role is None:
|
if default_role is None:
|
||||||
default = getattr(settings,
|
default = getattr(settings,
|
||||||
@ -528,12 +535,38 @@ class CreateProject(CommonQuotaWorkflow):
|
|||||||
self._update_project_members(request, data, project_id)
|
self._update_project_members(request, data, project_id)
|
||||||
if PROJECT_GROUP_ENABLED:
|
if PROJECT_GROUP_ENABLED:
|
||||||
self._update_project_groups(request, data, project_id)
|
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
|
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):
|
class UpdateProjectInfoAction(CreateProjectInfoAction):
|
||||||
enabled = forms.BooleanField(required=False, label=_("Enabled"))
|
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):
|
def __init__(self, request, initial, *args, **kwargs):
|
||||||
super(UpdateProjectInfoAction, self).__init__(
|
super(UpdateProjectInfoAction, self).__init__(
|
||||||
@ -608,7 +641,8 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
|||||||
return api.keystone.role_list(request)
|
return api.keystone.role_list(request)
|
||||||
|
|
||||||
def _update_project(self, request, data):
|
def _update_project(self, request, data):
|
||||||
# update project info
|
"""Update project info"""
|
||||||
|
domain_id = api.keystone.get_effective_domain_id(self.request)
|
||||||
try:
|
try:
|
||||||
project_id = data['project_id']
|
project_id = data['project_id']
|
||||||
return api.keystone.tenant_update(
|
return api.keystone.tenant_update(
|
||||||
@ -616,12 +650,14 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
|||||||
project_id,
|
project_id,
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
description=data['description'],
|
description=data['description'],
|
||||||
enabled=data['enabled'])
|
enabled=data['enabled'],
|
||||||
|
domain=domain_id)
|
||||||
except exceptions.Conflict:
|
except exceptions.Conflict:
|
||||||
msg = _('Project name "%s" is already used.') % data['name']
|
msg = _('Project name "%s" is already used.') % data['name']
|
||||||
self.failure_message = msg
|
self.failure_message = msg
|
||||||
return
|
return
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
LOG.debug('Project update failed: %s' % e)
|
||||||
exceptions.handle(request, ignore=True)
|
exceptions.handle(request, ignore=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -699,7 +735,22 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
|||||||
request, project=project_id)
|
request, project=project_id)
|
||||||
users_to_modify = len(users_roles)
|
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():
|
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
|
# Check if there have been any changes in the roles of
|
||||||
# Existing project members.
|
# Existing project members.
|
||||||
current_role_ids = list(users_roles[user_id])
|
current_role_ids = list(users_roles[user_id])
|
||||||
@ -854,8 +905,32 @@ class UpdateProject(CommonQuotaWorkflow, IdentityMixIn):
|
|||||||
if not ret:
|
if not ret:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ret = self._update_project_quota(request, data, project_id)
|
if api.keystone.is_cloud_admin(request):
|
||||||
if not ret:
|
ret = self._update_project_quota(request, data, project_id)
|
||||||
return False
|
if not ret:
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
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'
|
slug = 'roles'
|
||||||
policy_rules = (("identity", "identity:list_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
|
@staticmethod
|
||||||
def can_register():
|
def can_register():
|
||||||
return keystone.VERSIONS.active >= 3
|
return keystone.VERSIONS.active >= 3
|
||||||
|
@ -68,17 +68,26 @@ class BaseUserForm(forms.SelfHandlingForm):
|
|||||||
# the user has access to.
|
# the user has access to.
|
||||||
user_id = kwargs['initial'].get('id', None)
|
user_id = kwargs['initial'].get('id', None)
|
||||||
domain_id = kwargs['initial'].get('domain_id', None)
|
domain_id = kwargs['initial'].get('domain_id', None)
|
||||||
projects, has_more = api.keystone.tenant_list(request,
|
|
||||||
domain=domain_id,
|
try:
|
||||||
user=user_id)
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
for project in projects:
|
projects, has_more = api.keystone.tenant_list(
|
||||||
if project.enabled:
|
request, domain=domain_id)
|
||||||
project_choices.append((project.id, project.name))
|
else:
|
||||||
if not project_choices:
|
projects, has_more = api.keystone.tenant_list(
|
||||||
project_choices.insert(0, ('', _("No available projects")))
|
request, user=user_id)
|
||||||
elif len(project_choices) > 1:
|
|
||||||
project_choices.insert(0, ('', _("Select a project")))
|
for project in projects:
|
||||||
self.fields['project'].choices = project_choices
|
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"
|
ADD_PROJECT_URL = "horizon:identity:projects:create"
|
||||||
@ -135,7 +144,7 @@ class CreateUserForm(PasswordMixin, BaseUserForm):
|
|||||||
# password and confirm_password strings.
|
# password and confirm_password strings.
|
||||||
@sensitive_variables('data')
|
@sensitive_variables('data')
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
domain = api.keystone.get_default_domain(self.request)
|
domain = api.keystone.get_default_domain(self.request, False)
|
||||||
try:
|
try:
|
||||||
LOG.info('Creating user with name "%s"' % data['name'])
|
LOG.info('Creating user with name "%s"' % data['name'])
|
||||||
desc = data["description"]
|
desc = data["description"]
|
||||||
|
@ -20,9 +20,17 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
import horizon
|
import horizon
|
||||||
|
|
||||||
|
from openstack_dashboard.api import keystone
|
||||||
|
|
||||||
|
|
||||||
class Users(horizon.Panel):
|
class Users(horizon.Panel):
|
||||||
name = _("Users")
|
name = _("Users")
|
||||||
slug = 'users'
|
slug = 'users'
|
||||||
policy_rules = (("identity", "identity:get_user"),
|
policy_rules = (("identity", "identity:get_user"),
|
||||||
("identity", "identity:list_users"))
|
("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"
|
icon = "pencil"
|
||||||
policy_rules = (("identity", "identity:update_user"),
|
policy_rules = (("identity", "identity:update_user"),
|
||||||
("identity", "identity:list_projects"),)
|
("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):
|
def allowed(self, request, user):
|
||||||
return api.keystone.keystone_can_edit_user()
|
return api.keystone.keystone_can_edit_user()
|
||||||
@ -103,7 +104,8 @@ class ToggleEnabled(policy.PolicyTargetMixin, tables.BatchAction):
|
|||||||
)
|
)
|
||||||
classes = ("btn-toggle",)
|
classes = ("btn-toggle",)
|
||||||
policy_rules = (("identity", "identity:update_user"),)
|
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):
|
def allowed(self, request, user=None):
|
||||||
if not api.keystone.keystone_can_edit_user():
|
if not api.keystone.keystone_can_edit_user():
|
||||||
@ -137,7 +139,7 @@ class ToggleEnabled(policy.PolicyTargetMixin, tables.BatchAction):
|
|||||||
self.current_past_action = ENABLE
|
self.current_past_action = ENABLE
|
||||||
|
|
||||||
|
|
||||||
class DeleteUsersAction(tables.DeleteAction):
|
class DeleteUsersAction(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def action_present(count):
|
def action_present(count):
|
||||||
return ungettext_lazy(
|
return ungettext_lazy(
|
||||||
@ -256,6 +258,18 @@ class UsersTable(tables.DataTable):
|
|||||||
defaultfilters.capfirst),
|
defaultfilters.capfirst),
|
||||||
empty_value="False")
|
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):
|
class Meta(object):
|
||||||
name = "users"
|
name = "users"
|
||||||
verbose_name = _("Users")
|
verbose_name = _("Users")
|
||||||
|
@ -53,13 +53,20 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
if user.domain_id == domain_id]
|
if user.domain_id == domain_id]
|
||||||
return users
|
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):
|
def test_index(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
users = self._get_users(domain_id)
|
users = self._get_users(domain_id)
|
||||||
|
|
||||||
|
api.keystone.get_effective_domain_id(IgnoreArg()).AndReturn(domain_id)
|
||||||
|
|
||||||
api.keystone.user_list(IgnoreArg(),
|
api.keystone.user_list(IgnoreArg(),
|
||||||
domain=domain_id).AndReturn(users)
|
domain=domain_id).AndReturn(users)
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(USERS_INDEX_URL)
|
res = self.client.get(USERS_INDEX_URL)
|
||||||
@ -91,11 +98,19 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
role = self.roles.first()
|
role = self.roles.first()
|
||||||
|
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.MultipleTimes().AndReturn(domain)
|
.AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
api.keystone.get_default_domain(IgnoreArg(), False) \
|
||||||
domain=domain_id,
|
.AndReturn(domain)
|
||||||
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.user_create(IgnoreArg(),
|
api.keystone.user_create(IgnoreArg(),
|
||||||
name=user.name,
|
name=user.name,
|
||||||
description=user.description,
|
description=user.description,
|
||||||
@ -146,11 +161,19 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
role = self.roles.first()
|
role = self.roles.first()
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.MultipleTimes().AndReturn(domain)
|
.AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
api.keystone.get_default_domain(IgnoreArg(), False) \
|
||||||
domain=domain_id,
|
.AndReturn(domain)
|
||||||
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=user.id).AndReturn(
|
||||||
|
[self.tenants.list(), False])
|
||||||
|
|
||||||
api.keystone.user_create(IgnoreArg(),
|
api.keystone.user_create(IgnoreArg(),
|
||||||
name=user.name,
|
name=user.name,
|
||||||
description=user.description,
|
description=user.description,
|
||||||
@ -192,8 +215,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.MultipleTimes().AndReturn(domain)
|
.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.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||||
api.keystone.get_default_role(IgnoreArg()) \
|
api.keystone.get_default_role(IgnoreArg()) \
|
||||||
.AndReturn(self.roles.first())
|
.AndReturn(self.roles.first())
|
||||||
@ -224,8 +255,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.MultipleTimes().AndReturn(domain)
|
.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.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||||
api.keystone.get_default_role(IgnoreArg()) \
|
api.keystone.get_default_role(IgnoreArg()) \
|
||||||
.AndReturn(self.roles.first())
|
.AndReturn(self.roles.first())
|
||||||
@ -259,8 +298,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.MultipleTimes().AndReturn(domain)
|
.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.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||||
api.keystone.get_default_role(IgnoreArg()) \
|
api.keystone.get_default_role(IgnoreArg()) \
|
||||||
.AndReturn(self.roles.first())
|
.AndReturn(self.roles.first())
|
||||||
@ -299,10 +346,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
admin=True).AndReturn(user)
|
admin=True).AndReturn(user)
|
||||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||||
domain_id).AndReturn(domain)
|
domain_id).AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
|
||||||
domain=domain_id,
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
user=user.id) \
|
api.keystone.tenant_list(
|
||||||
.AndReturn([self.tenants.list(), False])
|
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),
|
api.keystone.user_update(IsA(http.HttpRequest),
|
||||||
user.id,
|
user.id,
|
||||||
email=user.email,
|
email=user.email,
|
||||||
@ -337,10 +390,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
admin=True).AndReturn(user)
|
admin=True).AndReturn(user)
|
||||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||||
domain_id).AndReturn(domain)
|
domain_id).AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
|
||||||
domain=domain_id,
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
user=user.id) \
|
api.keystone.tenant_list(
|
||||||
.AndReturn([self.tenants.list(), False])
|
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),
|
api.keystone.user_update(IsA(http.HttpRequest),
|
||||||
user.id,
|
user.id,
|
||||||
email=user.email,
|
email=user.email,
|
||||||
@ -376,8 +435,15 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
admin=True).AndReturn(user)
|
admin=True).AndReturn(user)
|
||||||
api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \
|
api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \
|
||||||
.AndReturn(domain)
|
.AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=user.id) \
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
.AndReturn([self.tenants.list(), False])
|
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)
|
||||||
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',
|
res, "form", 'password',
|
||||||
['Password must be between 8 and 18 characters.'])
|
['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):
|
def test_enable_user(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
@ -498,6 +566,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.user_update_enabled(IgnoreArg(),
|
api.keystone.user_update_enabled(IgnoreArg(),
|
||||||
user.id,
|
user.id,
|
||||||
True).AndReturn(user)
|
True).AndReturn(user)
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -506,7 +576,9 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
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):
|
def test_disable_user(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
@ -520,6 +592,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.user_update_enabled(IgnoreArg(),
|
api.keystone.user_update_enabled(IgnoreArg(),
|
||||||
user.id,
|
user.id,
|
||||||
False).AndReturn(user)
|
False).AndReturn(user)
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -528,7 +602,9 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
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):
|
def test_enable_disable_user_exception(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
@ -540,6 +616,8 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(users)
|
.AndReturn(users)
|
||||||
api.keystone.user_update_enabled(IgnoreArg(), user.id, True) \
|
api.keystone.user_update_enabled(IgnoreArg(), user.id, True) \
|
||||||
.AndRaise(self.exceptions.keystone)
|
.AndRaise(self.exceptions.keystone)
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||||
|
domain.name})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'users__toggle__%s' % user.id}
|
formData = {'action': 'users__toggle__%s' % user.id}
|
||||||
@ -547,7 +625,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.assertRedirectsNoFollow(res, USERS_INDEX_URL)
|
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):
|
def test_disabling_current_user(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
@ -555,6 +633,33 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
for i in range(0, 2):
|
for i in range(0, 2):
|
||||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||||
.AndReturn(users)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -565,7 +670,7 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
u'You cannot disable the user you are currently '
|
u'You cannot disable the user you are currently '
|
||||||
u'logged in as.')
|
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):
|
def test_delete_user_with_improper_permissions(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
domain_id = domain.id
|
domain_id = domain.id
|
||||||
@ -573,6 +678,33 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
for i in range(0, 2):
|
for i in range(0, 2):
|
||||||
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
api.keystone.user_list(IgnoreArg(), domain=domain_id) \
|
||||||
.AndReturn(users)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -662,10 +794,16 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
admin=True).AndReturn(user)
|
admin=True).AndReturn(user)
|
||||||
api.keystone.domain_get(IsA(http.HttpRequest),
|
api.keystone.domain_get(IsA(http.HttpRequest),
|
||||||
domain_id).AndReturn(domain)
|
domain_id).AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(),
|
|
||||||
domain=domain_id,
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
user=user.id) \
|
api.keystone.tenant_list(
|
||||||
.AndReturn([self.tenants.list(), False])
|
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),
|
api.keystone.user_update(IsA(http.HttpRequest),
|
||||||
user.id,
|
user.id,
|
||||||
email=user.email,
|
email=user.email,
|
||||||
@ -696,17 +834,27 @@ class SeleniumTests(test.SeleniumAdminTestCase):
|
|||||||
'tenant_list',
|
'tenant_list',
|
||||||
'get_default_role',
|
'get_default_role',
|
||||||
'role_list',
|
'role_list',
|
||||||
'user_list')})
|
'user_list',
|
||||||
|
'domain_lookup')})
|
||||||
def test_modal_create_user_with_passwords_not_matching(self):
|
def test_modal_create_user_with_passwords_not_matching(self):
|
||||||
domain = self._get_default_domain()
|
domain = self._get_default_domain()
|
||||||
|
|
||||||
api.keystone.get_default_domain(IgnoreArg()) \
|
api.keystone.get_default_domain(IgnoreArg()) \
|
||||||
.AndReturn(domain)
|
.MultipleTimes().AndReturn(domain)
|
||||||
api.keystone.tenant_list(IgnoreArg(), domain=None, user=None) \
|
|
||||||
.AndReturn([self.tenants.list(), False])
|
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.role_list(IgnoreArg()).AndReturn(self.roles.list())
|
||||||
api.keystone.user_list(IgnoreArg(), domain=None) \
|
api.keystone.user_list(IgnoreArg(), domain=None) \
|
||||||
.AndReturn(self.users.list())
|
.AndReturn(self.users.list())
|
||||||
|
api.keystone.domain_lookup(IgnoreArg()).AndReturn({None: None})
|
||||||
api.keystone.get_default_role(IgnoreArg()) \
|
api.keystone.get_default_role(IgnoreArg()) \
|
||||||
.AndReturn(self.roles.first())
|
.AndReturn(self.roles.first())
|
||||||
self.mox.ReplayAll()
|
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_password").send_keys("test")
|
||||||
self.selenium.find_element_by_id("id_confirm_password").send_keys("te")
|
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")
|
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"),
|
self.assertTrue(self._is_element_present("id_confirm_password_error"),
|
||||||
"Couldn't find password error element.")
|
"Couldn't find password error element.")
|
||||||
|
|
||||||
|
@ -50,9 +50,10 @@ class IndexView(tables.DataTableView):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
users = []
|
users = []
|
||||||
domain_context = self.request.session.get('domain_context', None)
|
|
||||||
if policy.check((("identity", "identity:list_users"),),
|
if policy.check((("identity", "identity:list_users"),),
|
||||||
self.request):
|
self.request):
|
||||||
|
domain_context = api.keystone.get_effective_domain_id(self.request)
|
||||||
try:
|
try:
|
||||||
users = api.keystone.user_list(self.request,
|
users = api.keystone.user_list(self.request,
|
||||||
domain=domain_context)
|
domain=domain_context)
|
||||||
@ -71,6 +72,11 @@ class IndexView(tables.DataTableView):
|
|||||||
else:
|
else:
|
||||||
msg = _("Insufficient privilege level to view user information.")
|
msg = _("Insufficient privilege level to view user information.")
|
||||||
messages.info(self.request, msg)
|
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
|
return users
|
||||||
|
|
||||||
|
|
||||||
@ -108,12 +114,18 @@ class UpdateView(forms.ModalFormView):
|
|||||||
user = self.get_object()
|
user = self.get_object()
|
||||||
domain_id = getattr(user, "domain_id", None)
|
domain_id = getattr(user, "domain_id", None)
|
||||||
domain_name = ''
|
domain_name = ''
|
||||||
# Retrieve the domain name where the project belong
|
# Retrieve the domain name where the project belongs
|
||||||
if api.keystone.VERSIONS.active >= 3:
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
try:
|
try:
|
||||||
domain = api.keystone.domain_get(self.request,
|
if policy.check((("identity", "identity:get_domain"),),
|
||||||
domain_id)
|
self.request):
|
||||||
domain_name = domain.name
|
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:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve project domain.'))
|
_('Unable to retrieve project domain.'))
|
||||||
@ -176,8 +188,14 @@ class DetailView(views.HorizonTemplateView):
|
|||||||
domain_name = ''
|
domain_name = ''
|
||||||
if api.keystone.VERSIONS.active >= 3:
|
if api.keystone.VERSIONS.active >= 3:
|
||||||
try:
|
try:
|
||||||
domain = api.keystone.domain_get(self.request, domain_id)
|
if policy.check((("identity", "identity:get_domain"),),
|
||||||
domain_name = domain.name
|
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:
|
except Exception:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve project domain.'))
|
_('Unable to retrieve project domain.'))
|
||||||
|
@ -21,4 +21,9 @@ class Project(horizon.Dashboard):
|
|||||||
name = _("Project")
|
name = _("Project")
|
||||||
slug = "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)
|
horizon.register(Project)
|
||||||
|
@ -66,7 +66,11 @@ WEBROOT = '/'
|
|||||||
|
|
||||||
# Overrides the default domain used when running on single-domain model
|
# Overrides the default domain used when running on single-domain model
|
||||||
# with Keystone V3. All entities will be created in the default domain.
|
# 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
|
# 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
|
# 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"),
|
policy_target_attrs = (("project_id", "tenant_id"),
|
||||||
("user_id", "user_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):
|
def get_policy_target(self, request, datum=None):
|
||||||
policy_target = {}
|
policy_target = {}
|
||||||
|
@ -36,6 +36,11 @@ def get_user_home(user):
|
|||||||
if dashboard is None:
|
if dashboard is None:
|
||||||
dashboard = horizon.get_default_dashboard()
|
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()
|
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