Remove db layer hard-code permission checks for quota_class_create/update

This patch removes db layer hard-code permission checks for
quota_class_create/update.

For v2 API, this patch adds back-comptiable permission checks at REST
API layer.

For v2.1 API, this patch adds new rule for update method.

Partially implements bp nova-api-policy-final-part

SecurityImpact
UpgradeImpact: Due to the db layer permission checks deleted, the policy
rule "os_compute_api:os-quota-class-sets:update" was updated with
a default that match the permission as before. Admin should be notified
to update their policy configuration to keep permission as before.

Change-Id: Icddc7e5cc1c11ab3d272f61a2ef623d3f750030c
This commit is contained in:
He Jie Xu 2015-03-02 08:42:11 +08:00
parent 1dbb322813
commit 725c54e60a
6 changed files with 29 additions and 25 deletions

View File

@ -337,7 +337,7 @@
"os_compute_api:os-quota-sets:update": "rule:admin_api", "os_compute_api:os-quota-sets:update": "rule:admin_api",
"os_compute_api:os-quota-sets:delete": "rule:admin_api", "os_compute_api:os-quota-sets:delete": "rule:admin_api",
"os_compute_api:os-quota-sets:detail": "rule:admin_api", "os_compute_api:os-quota-sets:detail": "rule:admin_api",
"os_compute_api:os-quota-class-sets": "", "os_compute_api:os-quota-class-sets:update": "rule:admin_api",
"os_compute_api:os-quota-class-sets:show": "is_admin:True or quota_class:%(quota_class)s", "os_compute_api:os-quota-class-sets:show": "is_admin:True or quota_class:%(quota_class)s",
"os_compute_api:os-quota-class-sets:discoverable": "", "os_compute_api:os-quota-class-sets:discoverable": "",
"os_compute_api:os-rescue": "", "os_compute_api:os-rescue": "",

View File

@ -94,13 +94,20 @@ class QuotaClassSetsController(wsgi.Controller):
msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys) msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
try:
# NOTE(alex_xu): back-compatible with db layer hard-code admin
# permission checks. This has to be left only for API v2.0 because
# this version has to be stable even if it means that only admins
# can call this method while the policy could be changed.
nova.context.require_admin_context(context)
except exception.AdminRequired:
raise webob.exc.HTTPForbidden()
for key, value in quota_class_set.items(): for key, value in quota_class_set.items():
try: try:
db.quota_class_update(context, quota_class, key, value) db.quota_class_update(context, quota_class, key, value)
except exception.QuotaClassNotFound: except exception.QuotaClassNotFound:
db.quota_class_create(context, quota_class, key, value) db.quota_class_create(context, quota_class, key, value)
except exception.AdminRequired:
raise webob.exc.HTTPForbidden()
values = QUOTAS.get_class_quotas(context, quota_class) values = QUOTAS.get_class_quotas(context, quota_class)
return self._format_quota_set(None, values) return self._format_quota_set(None, values)

View File

@ -14,7 +14,6 @@
# under the License. # under the License.
import six import six
import webob
from nova.api.openstack.compute.schemas.v3 import quota_classes from nova.api.openstack.compute.schemas.v3 import quota_classes
from nova.api.openstack import extensions from nova.api.openstack import extensions
@ -68,11 +67,11 @@ class QuotaClassSetsController(wsgi.Controller):
values = QUOTAS.get_class_quotas(context, id) values = QUOTAS.get_class_quotas(context, id)
return self._format_quota_set(id, values) return self._format_quota_set(id, values)
@extensions.expected_errors((403)) @extensions.expected_errors(())
@validation.schema(quota_classes.update) @validation.schema(quota_classes.update)
def update(self, req, id, body): def update(self, req, id, body):
context = req.environ['nova.context'] context = req.environ['nova.context']
authorize(context) authorize(context, action='update', target={'quota_class': id})
quota_class = id quota_class = id
for key, value in six.iteritems(body['quota_class_set']): for key, value in six.iteritems(body['quota_class_set']):
@ -80,8 +79,6 @@ class QuotaClassSetsController(wsgi.Controller):
db.quota_class_update(context, quota_class, key, value) db.quota_class_update(context, quota_class, key, value)
except exception.QuotaClassNotFound: except exception.QuotaClassNotFound:
db.quota_class_create(context, quota_class, key, value) db.quota_class_create(context, quota_class, key, value)
except exception.AdminRequired:
raise webob.exc.HTTPForbidden()
values = QUOTAS.get_class_quotas(context, quota_class) values = QUOTAS.get_class_quotas(context, quota_class)
return self._format_quota_set(None, values) return self._format_quota_set(None, values)

View File

@ -3166,7 +3166,6 @@ def quota_class_get_all_by_name(context, class_name):
return result return result
@require_admin_context
def quota_class_create(context, class_name, resource, limit): def quota_class_create(context, class_name, resource, limit):
quota_class_ref = models.QuotaClass() quota_class_ref = models.QuotaClass()
quota_class_ref.class_name = class_name quota_class_ref.class_name = class_name
@ -3176,7 +3175,6 @@ def quota_class_create(context, class_name, resource, limit):
return quota_class_ref return quota_class_ref
@require_admin_context
def quota_class_update(context, class_name, resource, limit): def quota_class_update(context, class_name, resource, limit):
result = model_query(context, models.QuotaClass, read_deleted="no").\ result = model_query(context, models.QuotaClass, read_deleted="no").\
filter_by(class_name=class_name).\ filter_by(class_name=class_name).\

View File

@ -105,20 +105,6 @@ class QuotaClassSetsTestV21(test.TestCase):
self.assertEqual(res_dict, body) self.assertEqual(res_dict, body)
def test_quotas_update_as_user(self):
body = {'quota_class_set': {'instances': 50, 'cores': 50,
'ram': 51200, 'floating_ips': 10,
'fixed_ips': -1, 'metadata_items': 128,
'injected_files': 5,
'injected_file_content_bytes': 10240,
'security_groups': 10,
'security_group_rules': 20,
'key_pairs': 100,
}}
self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
self.req, 'test_class', body=body)
def test_quotas_update_with_empty_body(self): def test_quotas_update_with_empty_body(self):
body = {} body = {}
self.assertRaises(self.validation_error, self.controller.update, self.assertRaises(self.validation_error, self.controller.update,
@ -162,6 +148,11 @@ class QuotaClassSetsTestV2(QuotaClassSetsTestV21):
self.assertRaises(webob.exc.HTTPForbidden, self.controller.show, self.assertRaises(webob.exc.HTTPForbidden, self.controller.show,
self.req, 'test_class') self.req, 'test_class')
def test_quotas_update_as_user(self):
body = {'quota_class_set': {}}
self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
self.req, 'test_class', body=body)
class QuotaClassesPolicyEnforcementV21(test.NoDBTestCase): class QuotaClassesPolicyEnforcementV21(test.NoDBTestCase):
@ -181,3 +172,14 @@ class QuotaClassesPolicyEnforcementV21(test.NoDBTestCase):
self.assertEqual( self.assertEqual(
"Policy doesn't allow %s to be performed." % rule_name, "Policy doesn't allow %s to be performed." % rule_name,
exc.format_message()) exc.format_message())
def test_update_policy_failed(self):
rule_name = "os_compute_api:os-quota-class-sets:update"
self.policy.set_rules({rule_name: "quota_class:non_fake"})
exc = self.assertRaises(
exception.PolicyNotAuthorized,
self.controller.update, self.req, fakes.FAKE_UUID,
body={'quota_class_set': {}})
self.assertEqual(
"Policy doesn't allow %s to be performed." % rule_name,
exc.format_message())

View File

@ -299,7 +299,7 @@ policy_data = """
"os_compute_api:os-quota-sets:detail": "", "os_compute_api:os-quota-sets:detail": "",
"os_compute_api:os-quota-sets:defaults": "", "os_compute_api:os-quota-sets:defaults": "",
"compute_extension:quota_classes": "", "compute_extension:quota_classes": "",
"os_compute_api:os-quota-class-sets": "", "os_compute_api:os-quota-class-sets:update": "",
"os_compute_api:os-quota-class-sets:show": "", "os_compute_api:os-quota-class-sets:show": "",
"compute_extension:rescue": "", "compute_extension:rescue": "",
"os_compute_api:os-rescue": "", "os_compute_api:os-rescue": "",