Role operations should not require list object permission

When using Keystone's policy.v3cloudsample.json policy file, a project admin is
supposed to be able to manage role assignments.  Unfortunately, a project admin
isn't allowed to perform these operations using python-openstackclient, as we
attempt to perform list operations for any of the object types specified (users,
groups, projects). This is done in an attempt to lookup the id of the object by
name, but we perform this list operation even when the user specifies everything
by id. This causes 403 errors.

This patch still attempts to look up the object id by name, but we catch the 403
and assume that the user specified an id if the list operation is not allowed.
This is similar to what we do with the --domain option for other commands.

Closes-bug: #1445528
Change-Id: Id95a8520e935c1092d5a22ecd8ea01f572334ac8
This commit is contained in:
Nathan Kinder 2015-04-16 19:12:45 -07:00
parent b72f2fb7ee
commit 4c107e6f1b
3 changed files with 108 additions and 49 deletions

View File

@ -17,6 +17,9 @@
from keystoneclient import exceptions as identity_exc
from keystoneclient.v3 import domains
from keystoneclient.v3 import groups
from keystoneclient.v3 import projects
from keystoneclient.v3 import users
from openstackclient.common import exceptions
from openstackclient.common import utils
@ -56,4 +59,58 @@ def find_domain(identity_client, name_or_id):
return dom
except identity_exc.Forbidden:
pass
return domains.Domain(None, {'id': name_or_id})
return domains.Domain(None, {'id': name_or_id, 'name': name_or_id})
def find_group(identity_client, name_or_id):
"""Find a group.
If the user does not have permissions to to perform a list groups call,
e.g., if the user is a project admin, assume that the group given is the
id rather than the name. This method is used by the role add command to
allow a role to be assigned to a group by a project admin who does not
have permission to list groups.
"""
try:
group = utils.find_resource(identity_client.groups, name_or_id)
if group is not None:
return group
except identity_exc.Forbidden:
pass
return groups.Group(None, {'id': name_or_id, 'name': name_or_id})
def find_project(identity_client, name_or_id):
"""Find a project.
If the user does not have permissions to to perform a list projects
call, e.g., if the user is a project admin, assume that the project
given is the id rather than the name. This method is used by the role
add command to allow a role to be assigned to a user by a project admin
who does not have permission to list projects.
"""
try:
project = utils.find_resource(identity_client.projects, name_or_id)
if project is not None:
return project
except identity_exc.Forbidden:
pass
return projects.Project(None, {'id': name_or_id, 'name': name_or_id})
def find_user(identity_client, name_or_id):
"""Find a user.
If the user does not have permissions to to perform a list users call,
e.g., if the user is a project admin, assume that the user given is the
id rather than the name. This method is used by the role add command to
allow a role to be assigned to a user by a project admin who does not
have permission to list users.
"""
try:
user = utils.find_resource(identity_client.users, name_or_id)
if user is not None:
return user
except identity_exc.Forbidden:
pass
return users.User(None, {'id': name_or_id, 'name': name_or_id})

View File

@ -26,6 +26,7 @@ from keystoneclient import exceptions as ksc_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
from openstackclient.identity import common
class AddRole(command.Command):
@ -78,12 +79,12 @@ class AddRole(command.Command):
)
if parsed_args.user and parsed_args.domain:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
identity_client.roles.grant(
@ -92,12 +93,12 @@ class AddRole(command.Command):
domain=domain.id,
)
elif parsed_args.user and parsed_args.project:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
identity_client.roles.grant(
@ -106,12 +107,12 @@ class AddRole(command.Command):
project=project.id,
)
elif parsed_args.group and parsed_args.domain:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
identity_client.roles.grant(
@ -120,12 +121,12 @@ class AddRole(command.Command):
domain=domain.id,
)
elif parsed_args.group and parsed_args.project:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
identity_client.roles.grant(
@ -240,24 +241,24 @@ class ListRole(lister.Lister):
identity_client = self.app.client_manager.identity
if parsed_args.user:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
elif parsed_args.group:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)
if parsed_args.domain:
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
elif parsed_args.project:
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
@ -370,12 +371,12 @@ class RemoveRole(command.Command):
)
if parsed_args.user and parsed_args.domain:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
identity_client.roles.revoke(
@ -384,12 +385,12 @@ class RemoveRole(command.Command):
domain=domain.id,
)
elif parsed_args.user and parsed_args.project:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
identity_client.roles.revoke(
@ -398,12 +399,12 @@ class RemoveRole(command.Command):
project=project.id,
)
elif parsed_args.group and parsed_args.domain:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
identity_client.roles.revoke(
@ -412,12 +413,12 @@ class RemoveRole(command.Command):
domain=domain.id,
)
elif parsed_args.group and parsed_args.project:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
identity_client.roles.revoke(

View File

@ -18,6 +18,7 @@ import logging
from cliff import lister
from openstackclient.common import utils
from openstackclient.identity import common
class ListRoleAssignment(lister.Lister):
@ -80,29 +81,29 @@ class ListRoleAssignment(lister.Lister):
user = None
if parsed_args.user:
user = utils.find_resource(
identity_client.users,
user = common.find_user(
identity_client,
parsed_args.user,
)
domain = None
if parsed_args.domain:
domain = utils.find_resource(
identity_client.domains,
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
project = None
if parsed_args.project:
project = utils.find_resource(
identity_client.projects,
project = common.find_project(
identity_client,
parsed_args.project,
)
group = None
if parsed_args.group:
group = utils.find_resource(
identity_client.groups,
group = common.find_group(
identity_client,
parsed_args.group,
)