Add granular policy rules for /resource_classes*

This adds policy rules or the various /resource_classes*
routes with the default being backward compatible with
existing deployments (requires role:admin).

The "non admin forbidden non json" gabbit had to be changed
because the policy check moved so the test would fail with a
406 response instead of 403.

Part of blueprint granular-placement-policy

Change-Id: I90ef62e8e7b9e75830ac60e0c5c7e92845ab4afe
This commit is contained in:
Matt Riedemann 2018-05-01 15:27:07 -04:00
parent 0a461979df
commit 77973ba7c5
6 changed files with 142 additions and 7 deletions

View File

@ -141,7 +141,10 @@ PER_ROUTE_POLICY = [
'/$',
# /resource_providers
# /resource_providers/{uuid}
'/resource_providers(/[A-Za-z0-9-]+)?$'
'/resource_providers(/[A-Za-z0-9-]+)?$',
# /resource_classes
# /resource_classes/{name}
'/resource_classes',
]

View File

@ -19,6 +19,7 @@ import webob
from nova.api.openstack.placement import exception
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.policies import resource_class as policies
from nova.api.openstack.placement.schemas import resource_class as schema
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
@ -62,6 +63,7 @@ def create_resource_class(req):
header pointing to the newly created resource class.
"""
context = req.environ['placement.context']
context.can(policies.CREATE)
data = util.extract_json(req.body, schema.POST_RC_SCHEMA_V1_2)
try:
@ -93,6 +95,7 @@ def delete_resource_class(req):
"""
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
context.can(policies.DELETE)
# The containing application will catch a not found here.
rc = rp_obj.ResourceClass.get_by_name(context, name)
try:
@ -119,6 +122,7 @@ def get_resource_class(req):
"""
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
context.can(policies.SHOW)
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
# The containing application will catch a not found here.
rc = rp_obj.ResourceClass.get_by_name(context, name)
@ -147,6 +151,7 @@ def list_resource_classes(req):
a collection of resource classes.
"""
context = req.environ['placement.context']
context.can(policies.LIST)
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
rcs = rp_obj.ResourceClassList.get_all(context)
@ -172,6 +177,7 @@ def update_resource_class(req):
"""
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
context.can(policies.UPDATE)
data = util.extract_json(req.body, schema.PUT_RC_SCHEMA_V1_2)
@ -210,6 +216,7 @@ def update_resource_class(req):
"""
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
context.can(policies.UPDATE)
# Use JSON validation to validation resource class name.
util.extract_json('{"name": "%s"}' % name, schema.PUT_RC_SCHEMA_V1_2)

View File

@ -13,6 +13,7 @@
import itertools
from nova.api.openstack.placement.policies import base
from nova.api.openstack.placement.policies import resource_class
from nova.api.openstack.placement.policies import resource_provider
@ -20,4 +21,5 @@ def list_rules():
return itertools.chain(
base.list_rules(),
resource_provider.list_rules(),
resource_class.list_rules(),
)

View File

@ -0,0 +1,81 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_policy import policy
from nova.api.openstack.placement.policies import base
PREFIX = 'placement:resource_classes:%s'
LIST = PREFIX % 'list'
CREATE = PREFIX % 'create'
SHOW = PREFIX % 'show'
UPDATE = PREFIX % 'update'
DELETE = PREFIX % 'delete'
rules = [
policy.DocumentedRuleDefault(
LIST,
base.RULE_ADMIN_API,
"List resource classes.",
[
{
'method': 'GET',
'path': '/resource_classes'
}
]),
policy.DocumentedRuleDefault(
CREATE,
base.RULE_ADMIN_API,
"Create resource class.",
[
{
'method': 'POST',
'path': '/resource_classes'
}
]),
policy.DocumentedRuleDefault(
SHOW,
base.RULE_ADMIN_API,
"Show resource class.",
[
{
'method': 'GET',
'path': '/resource_classes/{name}'
}
]),
policy.DocumentedRuleDefault(
UPDATE,
base.RULE_ADMIN_API,
"Update resource class.",
[
{
'method': 'PUT',
'path': '/resource_classes/{name}'
}
]),
policy.DocumentedRuleDefault(
DELETE,
base.RULE_ADMIN_API,
"Delete resource class.",
[
{
'method': 'DELETE',
'path': '/resource_classes/{name}'
}
]),
]
def list_rules():
return rules

View File

@ -0,0 +1,40 @@
# This tests the individual CRUD operations on /resource_classes
# using a non-admin user with an open policy configuration. The
# response validation is intentionally minimal.
fixtures:
- OpenPolicyFixture
defaults:
request_headers:
x-auth-token: user
accept: application/json
content-type: application/json
openstack-api-version: placement latest
tests:
- name: list resource classes
GET: /resource_classes
response_json_paths:
$.resource_classes.`len`: 12 # Number of standard resource classes
- name: create resource class
POST: /resource_classes
data:
name: CUSTOM_RES_CLASS_POLICY
status: 201
response_headers:
location: //resource_classes/CUSTOM_RES_CLASS_POLICY/
- name: show resource class
GET: /resource_classes/CUSTOM_RES_CLASS_POLICY
response_json_paths:
$.name: CUSTOM_RES_CLASS_POLICY
- name: update resource class
PUT: /resource_classes/CUSTOM_NEW_CLASS_POLICY
status: 201
- name: delete resource class
DELETE: /resource_classes/CUSTOM_NEW_CLASS_POLICY
status: 204

View File

@ -63,14 +63,16 @@ tests:
response_json_paths:
$.errors[0].title: Forbidden
- name: non admin forbidden non json
GET: /resource_classes
- name: post invalid non json
POST: /resource_classes
request_headers:
x-auth-token: user
accept: text/plain
status: 403
accept: text/plain
content-type: application/json
data:
name: FOO
status: 400
response_strings:
- admin required
- JSON does not validate
- name: post illegal characters in name
POST: /resource_classes