Restrict changing subscription of different tenant

In current tacker multi-tenant environment, users are able to perform
show and delete operations on subscriptions belonging to different
tenants.

This patch fixes the above problem by adding tenant_id in where clause
of the existing SQL queries used to show and delete subscriptions in
tacker.

Note: The functional test cases for the proposed changes are covered
in [1].

[1] https://review.opendev.org/c/openstack/tacker/+/846130

Closes-Bug: #1983575
Change-Id: I202bbcb2b8294993c4961afce0599e0737b0464d
This commit is contained in:
Manpreet Kaur 2022-08-09 14:53:26 +05:30
parent 693dce5f5d
commit d5a97c5ffb
3 changed files with 52 additions and 8 deletions

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Fixes `bug 1983575`_. In a multi-tenant environment, tacker disallows
deletion operation and showing of subscription details belonging to
different tenants.
.. _bug 1983575: https://bugs.launchpad.net/tacker/+bug/1983575

View File

@ -160,7 +160,12 @@ def _vnf_lcm_subscriptions_get(context,
@db_api.context_manager.reader
def _vnf_lcm_subscriptions_show(context, subscriptionId):
"""Query to retrieve desired subscription details
The SQL query fetches subscription details only when
the requester's tenant details match with the target
subscription.
"""
sql = text(
"select "
"t1.id,t1.callback_uri,t1.tenant_id,t2.filter "
@ -168,14 +173,17 @@ def _vnf_lcm_subscriptions_show(context, subscriptionId):
"(select distinct subscription_uuid,filter from vnf_lcm_filters) t2 "
"where t1.id = t2.subscription_uuid "
"and deleted = 0 "
"and t1.id = :subsc_id")
"and t1.id = :subsc_id "
"and t1.tenant_id = :tenant_id")
result_line = ""
try:
result = context.session.execute(sql, {'subsc_id': subscriptionId})
result = context.session.execute(sql, {'subsc_id': subscriptionId,
'tenant_id': context.project_id})
for line in result:
result_line = line
except exceptions.NotFound:
return ''
LOG.error('Subscription %(id) not found.', {"id": subscriptionId})
return None
except Exception as e:
raise e
return result_line
@ -208,15 +216,20 @@ def _get_by_subscriptionid(context, subscriptionsId):
sql = text("select id "
"from vnf_lcm_subscriptions "
"where id = :subsc_id "
"and deleted = 0 ")
"and deleted = 0 "
"and tenant_id = :tenant_id")
result_line = ""
try:
result = context.session.execute(sql, {'subsc_id': subscriptionsId})
result = context.session.execute(sql, {'subsc_id': subscriptionsId,
'tenant_id': context.project_id})
for line in result:
result_line = line
except exceptions.NotFound:
return ''
LOG.error('Subscription %(id) not found.', {"id": subscriptionsId})
return None
except Exception as e:
raise e
return result
return result_line
@db_api.context_manager.reader

View File

@ -204,6 +204,17 @@ class TestVnfLcmSubScriptions(SqlTestCase):
self.context, self.subscription.id)
self.assertTrue(filter, result)
@mock.patch.object(objects.vnf_lcm_subscriptions,
'_vnf_lcm_subscriptions_show')
def test_show_for_invalid_tenant_id(self, mock_vnf_lcm_subscriptions_show):
mock_vnf_lcm_subscriptions_show.return_value = None
self.context.tenant_id = 12345
subs_obj = objects.vnf_lcm_subscriptions.LccnSubscriptionRequest(
context=self.context)
result = subs_obj.vnf_lcm_subscriptions_show(self.context,
self.subscription.id)
self.assertIsNone(result)
@mock.patch.object(objects.vnf_lcm_subscriptions,
'_vnf_lcm_subscriptions_all')
def test_list(self, mock_vnf_lcm_subscriptions_all):
@ -277,6 +288,18 @@ class TestVnfLcmSubScriptions(SqlTestCase):
mock_vnf_lcm_subscriptions_destroy.assert_called_with(
self.context, self.subscription.id)
@mock.patch.object(objects.vnf_lcm_subscriptions,
'_destroy_vnf_lcm_subscription')
@mock.patch.object(objects.vnf_lcm_subscriptions, '_get_by_subscriptionid')
def test_destroy_for_invalid_tenant_id(self,
mock_get_by_subscriptionid,
mock_vnf_lcm_subscriptions_destroy):
mock_get_by_subscriptionid.result_value = None
self.context.tenant_id = 12345
self.subscription.destroy(self.context, self.subscription.id)
mock_vnf_lcm_subscriptions_destroy.assert_called_with(
self.context, self.subscription.id)
@mock.patch.object(objects.vnf_lcm_subscriptions,
'_vnf_lcm_subscriptions_get')
def test_get(self, mock_vnf_lcm_subscriptions_get):