summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--keystone/limit/controllers.py15
-rw-r--r--keystone/tests/unit/limit/test_backends.py10
-rw-r--r--keystone/tests/unit/test_limits.py68
-rw-r--r--lower-constraints.txt2
-rw-r--r--releasenotes/notes/bp-strict-two-level-model.yaml6
-rw-r--r--requirements.txt2
6 files changed, 92 insertions, 11 deletions
diff --git a/keystone/limit/controllers.py b/keystone/limit/controllers.py
index 440b55f94..a17bc865a 100644
--- a/keystone/limit/controllers.py
+++ b/keystone/limit/controllers.py
@@ -101,17 +101,22 @@ class LimitV3(controller.V3Controller):
limit_id, limit)
return LimitV3.wrap_member(request.context_dict, ref)
- @controller.filterprotected('service_id', 'region_id', 'resource_name')
+ @controller.filterprotected('service_id', 'region_id', 'resource_name',
+ 'project_id')
def list_limits(self, request, filters):
hints = LimitV3.build_driver_hints(request, filters)
- # TODO(wxy): Add system-scope check. If the request is system-scoped,
- # it can get all limits.
context = request.context
- if not context.is_admin and not ('admin' in context.roles):
+ project_id_filter = hints.get_exact_filter_by_name('project_id')
+ if project_id_filter:
+ if context.system_scope:
+ refs = PROVIDERS.unified_limit_api.list_limits(hints)
+ else:
+ refs = []
+ else:
project_id = context.project_id
if project_id:
hints.add_filter('project_id', project_id)
- refs = PROVIDERS.unified_limit_api.list_limits(hints)
+ refs = PROVIDERS.unified_limit_api.list_limits(hints)
return LimitV3.wrap_collection(request.context_dict, refs, hints=hints)
@controller.protected()
diff --git a/keystone/tests/unit/limit/test_backends.py b/keystone/tests/unit/limit/test_backends.py
index e971ff7a2..78dd8d7d7 100644
--- a/keystone/tests/unit/limit/test_backends.py
+++ b/keystone/tests/unit/limit/test_backends.py
@@ -589,7 +589,7 @@ class LimitTests(object):
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex)
limit_2 = unit.new_limit_ref(
- project_id=self.tenant_bar['id'],
+ project_id=self.tenant_baz['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=10, id=uuid.uuid4().hex)
@@ -597,23 +597,25 @@ class LimitTests(object):
hints = driver_hints.Hints()
hints.add_filter('service_id', self.service_one['id'])
- hints.add_filter('project_id', self.tenant_bar['id'])
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(2, len(res))
hints = driver_hints.Hints()
hints.add_filter('region_id', self.region_one['id'])
- hints.add_filter('project_id', self.tenant_bar['id'])
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(1, len(res))
self.assertDictEqual(limit_1, res[0])
hints = driver_hints.Hints()
hints.add_filter('resource_name', 'backup')
- hints.add_filter('project_id', self.tenant_bar['id'])
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(0, len(res))
+ hints = driver_hints.Hints()
+ hints.add_filter('project_id', self.tenant_bar['id'])
+ res = PROVIDERS.unified_limit_api.list_limits(hints)
+ self.assertEqual(1, len(res))
+
def test_list_limit_by_multi_filter_with_project_id(self):
limit_1 = unit.new_limit_ref(
project_id=self.tenant_bar['id'],
diff --git a/keystone/tests/unit/test_limits.py b/keystone/tests/unit/test_limits.py
index ff6d2f1a8..03903f3c1 100644
--- a/keystone/tests/unit/test_limits.py
+++ b/keystone/tests/unit/test_limits.py
@@ -726,6 +726,74 @@ class LimitsTestCase(test_v3.RestfulTestCase):
'resource_limit']:
self.assertEqual(limits[0][key], ref1[key])
+ def test_list_limit_with_project_id_filter(self):
+ # Create a new projects and a 'non-admin' role for test. The assignment
+ # is like:
+ #
+ # self.user -- admin -- self.project (default)
+ # self.user -- non-admin -- the new project
+ # self.user -- admin -- system
+ project_2 = unit.new_project_ref(domain_id=self.domain_id)
+ project_2_id = project_2['id']
+ PROVIDERS.resource_api.create_project(project_2_id, project_2)
+
+ role_2 = unit.new_role_ref(name='non-admin')
+ role_2_id = role_2['id']
+ PROVIDERS.role_api.create_role(role_2_id, role_2)
+
+ PROVIDERS.assignment_api.add_role_to_user_and_project(
+ self.user_id, project_2_id, role_2_id)
+ PROVIDERS.assignment_api.create_system_grant_for_user(
+ self.user_id, self.role['id'])
+
+ # create two limit in different projects for test.
+ ref1 = unit.new_limit_ref(project_id=self.project_id,
+ service_id=self.service_id,
+ region_id=self.region_id,
+ resource_name='volume')
+ ref2 = unit.new_limit_ref(project_id=project_2_id,
+ service_id=self.service_id2,
+ resource_name='snapshot')
+ self.post(
+ '/limits',
+ body={'limits': [ref1, ref2]},
+ expected_status=http_client.CREATED)
+
+ # non system scoped request will get the limits in its project.
+ r = self.get('/limits', expected_status=http_client.OK)
+ limits = r.result['limits']
+ self.assertEqual(1, len(limits))
+ self.assertEqual(self.project_id, limits[0]['project_id'])
+
+ r = self.get(
+ '/limits', expected_status=http_client.OK,
+ auth=self.build_authentication_request(
+ user_id=self.user['id'], password=self.user['password'],
+ project_id=project_2_id))
+ limits = r.result['limits']
+ self.assertEqual(1, len(limits))
+ self.assertEqual(project_2_id, limits[0]['project_id'])
+
+ # if non system scoped request contain project_id filter, keystone
+ # will return an empty list.
+ r = self.get(
+ '/limits?project_id=%s' % self.project_id,
+ expected_status=http_client.OK)
+ limits = r.result['limits']
+ self.assertEqual(0, len(limits))
+
+ # a system scoped request can specify the project_id filter
+ r = self.get(
+ '/limits?project_id=%s' % self.project_id,
+ expected_status=http_client.OK,
+ auth=self.build_authentication_request(
+ user_id=self.user['id'], password=self.user['password'],
+ system=True)
+ )
+ limits = r.result['limits']
+ self.assertEqual(1, len(limits))
+ self.assertEqual(self.project_id, limits[0]['project_id'])
+
def test_show_limit(self):
ref1 = unit.new_limit_ref(project_id=self.project_id,
service_id=self.service_id,
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 5b46d81a4..92633b5d6 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -63,7 +63,7 @@ os-testr==1.0.0
oslo.cache==1.26.0
oslo.concurrency==3.26.0
oslo.config==5.2.0
-oslo.context==2.19.2
+oslo.context==2.21.0
oslo.db==4.27.0
oslo.i18n==3.15.3
oslo.log==3.36.0
diff --git a/releasenotes/notes/bp-strict-two-level-model.yaml b/releasenotes/notes/bp-strict-two-level-model.yaml
index 964fa4ad8..7af8c551b 100644
--- a/releasenotes/notes/bp-strict-two-level-model.yaml
+++ b/releasenotes/notes/bp-strict-two-level-model.yaml
@@ -13,3 +13,9 @@ features:
Please ensure that the previous project and limit structure deployment in
your Keystone won't break this model before starting to use it.
+
+ - >
+ [`blueprint strict-two-level-model <https://blueprints.launchpad.net/keystone/+spec/strict-two-level-model>`_]
+ The `project_id` filter is added for listing limits. This filter is used
+ for system-scoped request only to fetch the specified project limits. Non
+ system-scoped request will get empty response body instead.
diff --git a/requirements.txt b/requirements.txt
index 83ad7ef5e..4ad163089 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -24,7 +24,7 @@ scrypt>=0.8.0 # BSD
oslo.cache>=1.26.0 # Apache-2.0
oslo.concurrency>=3.26.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
-oslo.context>=2.19.2 # Apache-2.0
+oslo.context>=2.21.0 # Apache-2.0
oslo.messaging>=5.29.0 # Apache-2.0
oslo.db>=4.27.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0