Implement secure RBAC for usage

This commit updates the policies for the usage resource in
placement to support read-only roles.

This is part of a broader community effort to support read-only roles
and implement secure, consistent default policies.

This commit also allows project readers to see project-specific usages,
resolve a long-standing policy TODO.

Co-Authored-By: Stephen Finucane <stephenfin@redhat.com>
Change-Id: I80afaae965d3c5f862320ac11a7d05db2f6b6553
This commit is contained in:
Lance Bragstad 2020-10-28 21:06:45 +00:00
parent 1f9529f8a0
commit 7a7365c7e0
5 changed files with 246 additions and 21 deletions

View File

@ -88,18 +88,17 @@ def get_total_usages(req):
sum/total of usages.
Return 404 Not Found if the wanted microversion does not match.
"""
project_id = req.GET.get('project_id')
user_id = req.GET.get('user_id')
context = req.environ['placement.context']
# TODO(mriedem): When we support non-admins to use GET /usages we
# should pass the project_id (and user_id?) from the query parameters
# into context.can() for the target.
context.can(policies.TOTAL_USAGES)
context.can(
policies.TOTAL_USAGES,
target={'project_id': project_id})
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
util.validate_query_params(req, schema.GET_USAGES_SCHEMA_1_9)
project_id = req.GET.get('project_id')
user_id = req.GET.get('user_id')
usages = usage_obj.get_all_by_project_user(context, project_id,
user_id=user_id)

View File

@ -19,6 +19,8 @@ RULE_ADMIN_API = 'rule:admin_api'
# let's continue using generic check strings.
SYSTEM_ADMIN = 'role:admin and system_scope:all'
SYSTEM_READER = 'role:reader and system_scope:all'
PROJECT_READER = 'role:reader and project_id:%(project_id)s'
PROJECT_READER_OR_SYSTEM_READER = f'({SYSTEM_READER}) or ({PROJECT_READER})'
rules = [
# "placement" is the default rule (action) used for all routes that do

View File

@ -11,6 +11,7 @@
# under the License.
from oslo_log import versionutils
from oslo_policy import policy
from placement.policies import base
@ -19,34 +20,49 @@ from placement.policies import base
PROVIDER_USAGES = 'placement:resource_providers:usages'
TOTAL_USAGES = 'placement:usages'
DEPRECATED_REASON = """
The usage API now supports a read-only role by default.
"""
deprecated_list_rp_usages = policy.DeprecatedRule(
name=PROVIDER_USAGES,
check_str=base.RULE_ADMIN_API
)
deprecated_list_total_usages = policy.DeprecatedRule(
name=TOTAL_USAGES,
check_str=base.RULE_ADMIN_API
)
rules = [
policy.DocumentedRuleDefault(
PROVIDER_USAGES,
base.RULE_ADMIN_API,
"List resource provider usages.",
[
name=PROVIDER_USAGES,
check_str=base.SYSTEM_READER,
description="List resource provider usages.",
operations=[
{
'method': 'GET',
'path': '/resource_providers/{uuid}/usages'
}
],
scope_types=['system']),
scope_types=['system'],
deprecated_rule=deprecated_list_rp_usages,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.DocumentedRuleDefault(
# TODO(mriedem): At some point we might set scope_types=['project']
# so that non-admin project-scoped token users can query usages for
# their project. The context.can() target will need to change as well
# in the actual policy enforcement check in the handler code.
TOTAL_USAGES,
base.RULE_ADMIN_API,
"List total resource usages for a given project.",
[
name=TOTAL_USAGES,
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
description="List total resource usages for a given project.",
operations=[
{
'method': 'GET',
'path': '/usages'
}
],
scope_types=['system'])
scope_types=['system', 'project'],
deprecated_rule=deprecated_list_total_usages,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
]

View File

@ -0,0 +1,54 @@
---
fixtures:
- LegacyRBACPolicyFixture
vars:
- &project_id 9520f97991e94f30a8dd205ef3ce735a
- &project_admin_headers
x-auth-token: user
x-roles: admin,member,reader
x-project-id: *project_id
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &project_member_headers
x-auth-token: user
x-roles: member,reader
x-project-id: *project_id
accept: application/json
content-type: application/json
openstack-api-version: placement latest
tests:
- name: project admin can create resource provider
POST: /resource_providers
request_headers: *project_admin_headers
data:
name: $ENVIRON['RP_NAME']
uuid: $ENVIRON['RP_UUID']
status: 200
- name: project member cannot list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *project_member_headers
status: 403
- name: project admin can list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *project_admin_headers
status: 200
response_json_paths:
usages: {}
- name: project member cannot get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *project_member_headers
status: 403
- name: project admin can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *project_admin_headers
status: 200
response_json_paths:
usages: {}

View File

@ -0,0 +1,154 @@
---
fixtures:
- SecureRBACPolicyFixture
vars:
- &project_id $ENVIRON['PROJECT_ID']
- &project_id_alt $ENVIRON['PROJECT_ID_ALT']
- &system_admin_headers
x-auth-token: user
x-roles: admin,member,reader
accept: application/json
content-type: application/json
openstack-api-version: placement latest
openstack-system-scope: all
- &system_reader_headers
x-auth-token: user
x-roles: reader
accept: application/json
content-type: application/json
openstack-api-version: placement latest
openstack-system-scope: all
- &project_admin_headers
x-auth-token: user
x-roles: admin,member,reader
x-project-id: *project_id
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &project_member_headers
x-auth-token: user
x-roles: member,reader
x-project-id: *project_id
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &project_reader_headers
x-auth-token: user
x-roles: reader
x-project-id: *project_id
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &alt_project_admin_headers
x-auth-token: user
x-roles: admin,member,reader
x-project-id: *project_id_alt
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &alt_project_member_headers
x-auth-token: user
x-roles: member,reader
x-project-id: *project_id_alt
accept: application/json
content-type: application/json
openstack-api-version: placement latest
- &alt_project_reader_headers
x-auth-token: user
x-roles: reader
x-project-id: *project_id_alt
accept: application/json
content-type: application/json
openstack-api-version: placement latest
tests:
- name: system admin can create resource provider
POST: /resource_providers
request_headers: *system_admin_headers
data:
name: $ENVIRON['RP_NAME']
uuid: $ENVIRON['RP_UUID']
status: 200
- name: project admin cannot list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *project_admin_headers
status: 403
- name: project member cannot list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *project_member_headers
status: 403
- name: project reader cannot list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *project_reader_headers
status: 403
- name: system reader can list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *system_reader_headers
status: 200
response_json_paths:
usages: {}
- name: system admin can list provider usage
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
request_headers: *system_admin_headers
status: 200
response_json_paths:
usages: {}
- name: project admin can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *project_admin_headers
status: 200
response_json_paths:
usages: {}
- name: project member can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *project_member_headers
status: 200
response_json_paths:
usages: {}
- name: project reader can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *project_reader_headers
status: 200
response_json_paths:
usages: {}
# Make sure users from other projects can't snoop around for usage on projects
# they have no business knowing about.
- name: project admin cannot get total usage for unauthorized project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *alt_project_admin_headers
status: 403
- name: project member cannot get total usage for unauthorized project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *alt_project_member_headers
status: 403
- name: project reader cannot get total usage for unauthorized project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *alt_project_reader_headers
status: 403
- name: system reader can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *system_reader_headers
status: 200
response_json_paths:
usages: {}
- name: system admin can get total usage for project
GET: /usages?project_id=$ENVIRON['PROJECT_ID']
request_headers: *system_admin_headers
status: 200
response_json_paths:
usages: {}