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:
parent
1dbb322813
commit
725c54e60a
@ -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": "",
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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).\
|
||||||
|
@ -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())
|
||||||
|
@ -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": "",
|
||||||
|
Loading…
Reference in New Issue
Block a user