]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
CSCO:Tenants not to access unshared n/w profiles
authorSaksham Varma <sakvarma@cisco.com>
Thu, 4 Sep 2014 21:53:50 +0000 (14:53 -0700)
committerSaksham Varma <sakvarma@cisco.com>
Wed, 26 Nov 2014 02:58:54 +0000 (18:58 -0800)
Ensure that a n1kv tenant who has no access to a network profile
belonging to some other tenant, is not allowed to modify that profile.
Currently, a tenant can create networks on any network profile if he
has the network profile id.

Change-Id: I53d767acceaa5e2c08e75e6f18847f659cda8d8b
Closes-Bug: 1365727

etc/neutron/plugins/cisco/cisco_plugins.ini
neutron/plugins/cisco/common/config.py
neutron/plugins/cisco/db/n1kv_db_v2.py
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py

index 17eae7378a86a30ad91e73d26043b4287a4a0552..a93bc7f1d9b2a432a87cea481b07953904ff305b 100644 (file)
 # (IntOpt) Timeout duration in seconds for the http request
 # Default value: 15
 # http_timeout = 15
+
+# (BoolOpt) Specify whether tenants are restricted from accessing network
+# profiles belonging to other tenants.
+# Default value: True, indicating other tenants cannot access network
+# profiles belonging to a tenant.
+#
+# restrict_network_profiles = True
index dcba6b64da3bfe3c9da07db25c2f1109c8e76957..359dd11d26af572e13577d993f0e9025561d47dc 100644 (file)
@@ -70,6 +70,10 @@ cisco_n1k_opts = [
                help=_("Number of threads to use to make HTTP requests")),
     cfg.IntOpt('http_timeout', default=15,
                help=_("N1K http timeout duration in seconds")),
+    cfg.BoolOpt('restrict_network_profiles', default=True,
+               help=_("Restrict tenants from accessing network profiles "
+                      "belonging to some other tenant")),
+
 ]
 
 cfg.CONF.register_opts(cisco_opts, "CISCO")
index e87d2552b1ff4e890c4e7d64da584ca8b1a4c9ec..7b434d3fccee323eea3888f8191e947cc1d87c0e 100644 (file)
@@ -516,7 +516,7 @@ def reserve_vxlan(db_session, network_profile):
         raise n_exc.NoNetworkAvailable()
 
 
-def alloc_network(db_session, network_profile_id):
+def alloc_network(db_session, network_profile_id, tenant_id):
     """
     Allocate network using first available free segment ID in segment range.
 
@@ -525,7 +525,7 @@ def alloc_network(db_session, network_profile_id):
     """
     with db_session.begin(subtransactions=True):
         network_profile = get_network_profile(db_session,
-                                              network_profile_id)
+                                              network_profile_id, tenant_id)
         if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN:
             return reserve_vlan(db_session, network_profile)
         if network_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY:
@@ -785,12 +785,12 @@ def create_network_profile(db_session, network_profile):
         return net_profile
 
 
-def delete_network_profile(db_session, id):
+def delete_network_profile(db_session, id, tenant_id=None):
     """Delete Network Profile."""
     LOG.debug("delete_network_profile()")
     with db_session.begin(subtransactions=True):
         try:
-            network_profile = get_network_profile(db_session, id)
+            network_profile = get_network_profile(db_session, id, tenant_id)
             db_session.delete(network_profile)
             (db_session.query(n1kv_models_v2.ProfileBinding).
              filter_by(profile_id=id).delete())
@@ -799,21 +799,27 @@ def delete_network_profile(db_session, id):
             raise c_exc.ProfileTenantBindingNotFound(profile_id=id)
 
 
-def update_network_profile(db_session, id, network_profile):
+def update_network_profile(db_session, id, network_profile, tenant_id=None):
     """Update Network Profile."""
     LOG.debug("update_network_profile()")
     with db_session.begin(subtransactions=True):
-        profile = get_network_profile(db_session, id)
+        profile = get_network_profile(db_session, id, tenant_id)
         profile.update(network_profile)
         return profile
 
 
-def get_network_profile(db_session, id):
+def get_network_profile(db_session, id, tenant_id=None):
     """Get Network Profile."""
     LOG.debug("get_network_profile()")
+    if tenant_id and c_conf.CISCO_N1K.restrict_network_profiles:
+        if _profile_binding_exists(db_session=db_session,
+                                   tenant_id=tenant_id,
+                                   profile_id=id,
+                                   profile_type=c_const.NETWORK) is None:
+            raise c_exc.ProfileTenantBindingNotFound(profile_id=id)
     try:
-        return db_session.query(
-            n1kv_models_v2.NetworkProfile).filter_by(id=id).one()
+        return db_session.query(n1kv_models_v2.NetworkProfile).filter_by(
+                   id=id).one()
     except exc.NoResultFound:
         raise c_exc.NetworkProfileNotFound(profile=id)
 
@@ -1085,10 +1091,12 @@ class NetworkProfile_db_mixin(object):
         """
         # Check whether the network profile is in use.
         if self._segment_in_use(context.session,
-                                get_network_profile(context.session, id)):
+                                get_network_profile(context.session, id,
+                                                    context.tenant_id)):
             raise c_exc.NetworkProfileInUse(profile=id)
         # Delete and return the network profile if it is not in use.
-        _profile = delete_network_profile(context.session, id)
+        _profile = delete_network_profile(context.session, id,
+                                          context.tenant_id)
         return self._make_network_profile_dict(_profile)
 
     def update_network_profile(self, context, id, network_profile):
@@ -1105,7 +1113,8 @@ class NetworkProfile_db_mixin(object):
         # Flag to check whether network profile is updated or not.
         is_updated = False
         p = network_profile["network_profile"]
-        original_net_p = get_network_profile(context.session, id)
+        original_net_p = get_network_profile(context.session, id,
+                                             context.tenant_id)
         # Update network profile to tenant id binding.
         if context.is_admin and c_const.ADD_TENANTS in p:
             profile_bindings = _get_profile_bindings_by_uuid(context.session,
@@ -1138,7 +1147,8 @@ class NetworkProfile_db_mixin(object):
             p.get("segment_range") != original_net_p.segment_range):
             if not self._segment_in_use(context.session, original_net_p):
                 delete_segment_allocations(context.session, original_net_p)
-                updated_net_p = update_network_profile(context.session, id, p)
+                updated_net_p = update_network_profile(context.session, id, p,
+                                                       context.tenant_id)
                 self._validate_segment_range_uniqueness(context,
                                                         updated_net_p, id)
                 if original_net_p.segment_type == c_const.NETWORK_TYPE_VLAN:
@@ -1163,7 +1173,8 @@ class NetworkProfile_db_mixin(object):
         # Return network profile if it is successfully updated.
         if is_updated:
             return self._make_network_profile_dict(
-                update_network_profile(context.session, id, p))
+                update_network_profile(context.session, id, p,
+                                       context.tenant_id))
 
     def get_network_profile(self, context, id, fields=None):
         """
@@ -1175,7 +1186,7 @@ class NetworkProfile_db_mixin(object):
                         profile dictionary. Only these fields will be returned
         :returns: network profile dictionary
         """
-        profile = get_network_profile(context.session, id)
+        profile = get_network_profile(context.session, id, context.tenant_id)
         return self._make_network_profile_dict(profile, fields)
 
     def get_network_profiles(self, context, filters=None, fields=None):
@@ -1230,7 +1241,7 @@ class NetworkProfile_db_mixin(object):
         :returns: true if network profile exist else False
         """
         try:
-            get_network_profile(context.session, id)
+            get_network_profile(context.session, id, context.tenant_id)
             return True
         except c_exc.NetworkProfileNotFound(profile=id):
             return False
index 8bafe9a58d3ca628e0cb2d8ce3dd6e6e50c93bab..af33f70c6bee71c7baec63cf1627786550030523 100644 (file)
@@ -683,7 +683,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         LOG.debug('_send_update_network_request: %s', network['id'])
         db_session = context.session
         profile = n1kv_db_v2.get_network_profile(
-            db_session, network[n1kv.PROFILE_ID])
+            db_session, network[n1kv.PROFILE_ID], context.tenant_id)
         n1kvclient = n1kv_client.Client()
         body = {'description': network['name'],
                 'id': network['id'],
@@ -882,7 +882,8 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                 # tenant network
                 (physical_network, network_type, segmentation_id,
                     multicast_ip) = n1kv_db_v2.alloc_network(session,
-                                                             profile_id)
+                                                             profile_id,
+                                                             context.tenant_id)
                 LOG.debug('Physical_network %(phy_net)s, '
                           'seg_type %(net_type)s, '
                           'seg_id %(seg_id)s, '
index d0b272292bd4d686a9cb9dc15551ab0b7104e059..3f3a7d8da1b910c544d9a2e4f6483966db0876e6 100644 (file)
@@ -44,6 +44,8 @@ from neutron.tests.unit import test_l3_schedulers
 PHYS_NET = 'some-phys-net'
 VLAN_MIN = 100
 VLAN_MAX = 110
+TENANT_NOT_ADMIN = 'not_admin'
+TENANT_TEST = 'test'
 
 
 class FakeResponse(object):
@@ -159,6 +161,12 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
             profile['physical_network'] = PHYS_NET
             net_p = n1kv_db_v2.create_network_profile(db_session, profile)
             n1kv_db_v2.sync_vlan_allocations(db_session, net_p)
+        n1kv_db_v2.create_profile_binding(db_session, self.tenant_id,
+                                          net_p['id'], c_const.NETWORK)
+        n1kv_db_v2.create_profile_binding(db_session, TENANT_NOT_ADMIN,
+                                          net_p['id'], c_const.NETWORK)
+        n1kv_db_v2.create_profile_binding(db_session, TENANT_TEST,
+                                          net_p['id'], c_const.NETWORK)
         return net_p
 
     def setUp(self, ext_mgr=NetworkProfileTestExtensionManager()):
@@ -557,7 +565,7 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
         self.new_create_request('network_profiles', net_p_dict)
         bindings = (db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
                     profile_type="network"))
-        self.assertEqual(bindings.count(), 0)
+        self.assertEqual(3, bindings.count())
 
     def test_create_network_profile_with_old_add_tenant_fail(self):
         data = self._prepare_net_profile_data('vlan')
@@ -657,6 +665,52 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
                                                 net_p['network_profile']['id'])
         self.assertIsNotNone(tenant4)
 
+    def test_get_network_profile_restricted(self):
+        c_conf.CONF.set_override('restrict_network_profiles', True,
+                                 'CISCO_N1K')
+        ctx1 = context.Context(user_id='admin',
+                        tenant_id='tenant1',
+                        is_admin=True)
+        sess1 = db.get_session()
+        net_p = self._make_test_profile(name='netp1')
+        n1kv_db_v2.create_profile_binding(sess1, ctx1.tenant_id,
+                                          net_p['id'], c_const.NETWORK)
+        #network profile binding with creator tenant should always exist
+        profile = n1kv_db_v2.get_network_profile(sess1, net_p['id'],
+                                                 ctx1.tenant_id)
+        self.assertIsNotNone(profile)
+        ctx2 = context.Context(user_id='non_admin',
+                               tenant_id='tenant2',
+                               is_admin=False)
+        sess2 = db.get_session()
+        self.assertRaises(c_exc.ProfileTenantBindingNotFound,
+                          n1kv_db_v2.get_network_profile,
+                          sess2, net_p['id'], ctx2.tenant_id)
+
+    def test_get_network_profile_unrestricted(self):
+        c_conf.CONF.set_override('restrict_network_profiles', False,
+                                 'CISCO_N1K')
+        ctx1 = context.Context(user_id='admin',
+                               tenant_id='tenant1',
+                               is_admin=True)
+        sess1 = db.get_session()
+        net_p = self._make_test_profile(name='netp1')
+        n1kv_db_v2.create_profile_binding(sess1, ctx1.tenant_id,
+                                          net_p['id'], c_const.NETWORK)
+        # network profile binding with creator tenant should always exist
+        profile = n1kv_db_v2.get_network_profile(sess1, net_p['id'],
+                                                 ctx1.tenant_id)
+        self.assertIsNotNone(profile)
+        ctx2 = context.Context(user_id='non_admin',
+                               tenant_id='tenant2',
+                               is_admin=False)
+        sess2 = db.get_session()
+        profile = n1kv_db_v2.get_network_profile(sess2, net_p['id'],
+                                                 ctx2.tenant_id)
+        #network profile will be returned even though the profile is
+        #not bound to tenant of sess2
+        self.assertIsNotNone(profile)
+
 
 class TestN1kvBasicGet(test_plugin.TestBasicGet,
                        N1kvPluginTestCase):