diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 14bf8676ac9e..8dcb44bba6ee 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -386,6 +386,10 @@ class Executor(wsgi.Application): LOG.debug(_('InvalidPortRange raised: %s'), unicode(ex), context=context) return self._error(req, context, type(ex).__name__, unicode(ex)) + except exception.NotAuthorized as ex: + LOG.info(_('NotAuthorized raised: %s'), unicode(ex), + context=context) + return self._error(req, context, type(ex).__name__, unicode(ex)) except Exception as ex: extra = {'environment': req.environ} LOG.exception(_('Unexpected error raised: %s'), unicode(ex), diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index d078b26c6527..8b5b19c2120f 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -144,6 +144,8 @@ class Floating_ips(extensions.ExtensionDescriptor): address) except exception.ApiError, e: raise webob.exc.HTTPBadRequest(explanation=e.message) + except exception.NotAuthorized, e: + raise webob.exc.HTTPUnauthorized() return webob.Response(status_int=202) @@ -162,7 +164,10 @@ class Floating_ips(extensions.ExtensionDescriptor): floating_ip = self.network_api.get_floating_ip_by_ip(context, address) if floating_ip.get('fixed_ip'): - self.network_api.disassociate_floating_ip(context, address) + try: + self.network_api.disassociate_floating_ip(context, address) + except exception.NotAuthorized, e: + raise webob.exc.HTTPUnauthorized() return webob.Response(status_int=202) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5889f5fc3a2c..322699939a21 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -533,7 +533,6 @@ def floating_ip_fixed_ip_associate(context, floating_address, fixed_address, host): session = get_session() with session.begin(): - # TODO(devcamcar): How to ensure floating_id belongs to user? floating_ip_ref = floating_ip_get_by_address(context, floating_address, session=session) @@ -549,7 +548,6 @@ def floating_ip_fixed_ip_associate(context, floating_address, def floating_ip_deallocate(context, address): session = get_session() with session.begin(): - # TODO(devcamcar): How to ensure floating id belongs to user? floating_ip_ref = floating_ip_get_by_address(context, address, session=session) @@ -563,7 +561,6 @@ def floating_ip_deallocate(context, address): def floating_ip_destroy(context, address): session = get_session() with session.begin(): - # TODO(devcamcar): Ensure address belongs to user. floating_ip_ref = floating_ip_get_by_address(context, address, session=session) @@ -574,8 +571,6 @@ def floating_ip_destroy(context, address): def floating_ip_disassociate(context, address): session = get_session() with session.begin(): - # TODO(devcamcar): Ensure address belongs to user. - # Does get_floating_ip_by_address handle this? floating_ip_ref = floating_ip_get_by_address(context, address, session=session) @@ -644,17 +639,23 @@ def floating_ip_get_all_by_project(context, project_id): @require_context def floating_ip_get_by_address(context, address, session=None): - # TODO(devcamcar): Ensure the address belongs to user. if not session: session = get_session() result = session.query(models.FloatingIp).\ - options(joinedload_all('fixed_ip.network')).\ - filter_by(address=address).\ - filter_by(deleted=can_read_deleted(context)).\ - first() + options(joinedload_all('fixed_ip.network')).\ + filter_by(address=address).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + if not result: raise exception.FloatingIpNotFoundForAddress(address=address) + + # If the floating IP has a project ID set, check to make sure + # the non-admin user has access. + if result.project_id and is_user_context(context): + authorize_project_context(context, result.project_id) + return result diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py index 0744f0a11bbd..d4e08b30307d 100644 --- a/nova/tests/api/openstack/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/contrib/test_floating_ips.py @@ -278,7 +278,7 @@ class FloatingIpTest(test.TestCase): req.body = json.dumps(body) req.headers["content-type"] = "application/json" resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 400) + self.assertEqual(resp.status_int, 401) def test_associate_floating_ip_to_instance_no_project_id(self): def fake_fixed_ip_get_by_address(ctx, address, session=None): diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 7aa49d3909f8..9c28c8660f29 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -447,6 +447,52 @@ class VlanNetworkTestCase(test.TestCase): self.network.add_fixed_ip_to_instance(self.context, 1, HOST, networks[0]['id']) + def test_ip_association_and_allocation_of_other_project(self): + """Makes sure that we cannot deallocaate or disassociate + a public ip of other project""" + + context1 = context.RequestContext('user', 'project1') + context2 = context.RequestContext('user', 'project2') + + address = '1.2.3.4' + float_addr = db.floating_ip_create(context1.elevated(), + {'address': address, + 'project_id': context1.project_id}) + + instance = db.instance_create(context1, + {'project_id': 'project1'}) + + fix_addr = db.fixed_ip_associate_pool(context1.elevated(), + 1, instance['id']) + + # Associate the IP with non-admin user context + self.assertRaises(exception.NotAuthorized, + self.network.associate_floating_ip, + context2, + float_addr, + fix_addr) + + # Deallocate address from other project + self.assertRaises(exception.NotAuthorized, + self.network.deallocate_floating_ip, + context2, + float_addr) + + # Now Associates the address to the actual project + self.network.associate_floating_ip(context1, float_addr, fix_addr) + + # Now try dis-associating from other project + self.assertRaises(exception.NotAuthorized, + self.network.disassociate_floating_ip, + context2, + float_addr) + + # Clean up the ip addresses + self.network.deallocate_floating_ip(context1, float_addr) + self.network.deallocate_fixed_ip(context1, fix_addr) + db.floating_ip_destroy(context1.elevated(), float_addr) + db.fixed_ip_disassociate(context1.elevated(), fix_addr) + class CommonNetworkTestCase(test.TestCase):