diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-01-24 19:32:49 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-01-24 19:32:49 +0000 |
| commit | 8dfd968e83cbcdc0796caa44289144d38a6a5ce8 (patch) | |
| tree | e0e2e571a9aae49fc71736ad034d8fb6ed31b0b0 /nova/api | |
| parent | c88771d8202f9c1c3f7a7260dea200d902b3c3aa (diff) | |
| parent | 9cb5f547dc6f3242edf393928dbc14b7cbfbbdd4 (diff) | |
| download | nova-8dfd968e83cbcdc0796caa44289144d38a6a5ce8.tar.gz nova-8dfd968e83cbcdc0796caa44289144d38a6a5ce8.tar.xz nova-8dfd968e83cbcdc0796caa44289144d38a6a5ce8.zip | |
Merge "Remove admin_only ext attr in favor of authz"
Diffstat (limited to 'nova/api')
33 files changed, 246 insertions, 113 deletions
diff --git a/nova/api/mapper.py b/nova/api/mapper.py index cd26e06ee..aaa59f97f 100644 --- a/nova/api/mapper.py +++ b/nova/api/mapper.py @@ -32,9 +32,6 @@ from nova import wsgi as base_wsgi LOG = logging.getLogger('nova.api.openstack.compute') FLAGS = flags.FLAGS -flags.DEFINE_bool('allow_admin_api', - False, - 'When True, this API service will accept admin operations.') flags.DEFINE_bool('allow_instance_snapshots', True, 'When True, this API service will permit instance snapshot operations.') diff --git a/nova/api/openstack/compute/__init__.py b/nova/api/openstack/compute/__init__.py index 9e0509e71..f074ac941 100644 --- a/nova/api/openstack/compute/__init__.py +++ b/nova/api/openstack/compute/__init__.py @@ -42,9 +42,6 @@ from nova import wsgi as base_wsgi LOG = logging.getLogger('nova.api.openstack.compute') FLAGS = flags.FLAGS -flags.DEFINE_bool('allow_admin_api', - False, - 'When True, this API service will accept admin operations.') flags.DEFINE_bool('allow_instance_snapshots', True, 'When True, this API service will permit instance snapshot operations.') diff --git a/nova/api/openstack/compute/contrib/accounts.py b/nova/api/openstack/compute/contrib/accounts.py index 9253c037d..939912942 100644 --- a/nova/api/openstack/compute/contrib/accounts.py +++ b/nova/api/openstack/compute/contrib/accounts.py @@ -26,6 +26,7 @@ from nova import log as logging FLAGS = flags.FLAGS LOG = logging.getLogger('nova.api.openstack.compute.contrib.accounts') +authorize = extensions.extension_authorizer('compute', 'accounts') class AccountTemplate(xmlutil.TemplateBuilder): @@ -51,23 +52,18 @@ class Controller(object): def __init__(self): self.manager = manager.AuthManager() - def _check_admin(self, context): - """We cannot depend on the db layer to check for admin access - for the auth manager, so we do it here""" - if not context.is_admin: - raise exception.AdminRequired() - def index(self, req): raise webob.exc.HTTPNotImplemented() @wsgi.serializers(xml=AccountTemplate) def show(self, req, id): """Return data about the given account id""" + authorize(req.environ['nova.context']) account = self.manager.get_project(id) return dict(account=_translate_keys(account)) def delete(self, req, id): - self._check_admin(req.environ['nova.context']) + authorize(req.environ['nova.context']) self.manager.delete_project(id) return {} @@ -79,7 +75,7 @@ class Controller(object): @wsgi.serializers(xml=AccountTemplate) def update(self, req, id, body): """This is really create or update.""" - self._check_admin(req.environ['nova.context']) + authorize(req.environ['nova.context']) description = body['account'].get('description') manager = body['account'].get('manager') try: @@ -97,11 +93,8 @@ class Accounts(extensions.ExtensionDescriptor): alias = "os-accounts" namespace = "http://docs.openstack.org/compute/ext/accounts/api/v1.1" updated = "2011-12-23T00:00:00+00:00" - admin_only = True def get_resources(self): #TODO(bcwaldon): This should be prefixed with 'os-' - res = extensions.ResourceExtension('accounts', - Controller()) - + res = extensions.ResourceExtension('accounts', Controller()) return [res] diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index b44f16a1e..cbd54117d 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -30,6 +30,7 @@ from nova.scheduler import api as scheduler_api FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.openstack.compute.contrib.admin_actions") +authorize = extensions.extension_authorizer('compute', 'admin_actions') class AdminActionsController(wsgi.Controller): @@ -45,6 +46,7 @@ class AdminActionsController(wsgi.Controller): def _pause(self, req, id, body): """Permit Admins to pause the server""" ctxt = req.environ['nova.context'] + authorize(ctxt) try: server = self.compute_api.get(ctxt, id) self.compute_api.pause(ctxt, server) @@ -63,6 +65,7 @@ class AdminActionsController(wsgi.Controller): def _unpause(self, req, id, body): """Permit Admins to unpause the server""" ctxt = req.environ['nova.context'] + authorize(ctxt) try: server = self.compute_api.get(ctxt, id) self.compute_api.unpause(ctxt, server) @@ -81,6 +84,7 @@ class AdminActionsController(wsgi.Controller): def _suspend(self, req, id, body): """Permit admins to suspend the server""" context = req.environ['nova.context'] + authorize(context) try: server = self.compute_api.get(context, id) self.compute_api.suspend(context, server) @@ -99,6 +103,7 @@ class AdminActionsController(wsgi.Controller): def _resume(self, req, id, body): """Permit admins to resume the server from suspend""" context = req.environ['nova.context'] + authorize(context) try: server = self.compute_api.get(context, id) self.compute_api.resume(context, server) @@ -117,6 +122,7 @@ class AdminActionsController(wsgi.Controller): def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, id) self.compute_api.resize(req.environ['nova.context'], instance) @@ -134,6 +140,7 @@ class AdminActionsController(wsgi.Controller): def _reset_network(self, req, id, body): """Permit admins to reset networking on an server""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, id) self.compute_api.reset_network(context, instance) @@ -149,6 +156,7 @@ class AdminActionsController(wsgi.Controller): def _inject_network_info(self, req, id, body): """Permit admins to inject network info into a server""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, id) self.compute_api.inject_network_info(context, instance) @@ -166,6 +174,7 @@ class AdminActionsController(wsgi.Controller): def _lock(self, req, id, body): """Permit admins to lock a server""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, id) self.compute_api.lock(context, instance) @@ -183,6 +192,7 @@ class AdminActionsController(wsgi.Controller): def _unlock(self, req, id, body): """Permit admins to lock a server""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, id) self.compute_api.unlock(context, instance) @@ -207,6 +217,7 @@ class AdminActionsController(wsgi.Controller): """ context = req.environ["nova.context"] + authorize(context) try: entity = body["createBackup"] @@ -273,7 +284,6 @@ class Admin_actions(extensions.ExtensionDescriptor): alias = "os-admin-actions" namespace = "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1" updated = "2011-09-20T00:00:00+00:00" - admin_only = True def get_controller_extensions(self): controller = AdminActionsController() diff --git a/nova/api/openstack/compute/contrib/cloudpipe.py b/nova/api/openstack/compute/contrib/cloudpipe.py index 17bfa810b..1cf47a2a9 100644 --- a/nova/api/openstack/compute/contrib/cloudpipe.py +++ b/nova/api/openstack/compute/contrib/cloudpipe.py @@ -32,6 +32,7 @@ from nova import utils FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.openstack.compute.contrib.cloudpipe") +authorize = extensions.extension_authorizer('compute', 'cloudpipe') class CloudpipeTemplate(xmlutil.TemplateBuilder): @@ -120,6 +121,7 @@ class CloudpipeController(object): """ ctxt = req.environ['nova.context'] + authorize(ctxt) params = body.get('cloudpipe', {}) project_id = params.get('project_id', ctxt.project_id) instance = self._get_cloudpipe_for_project(ctxt, project_id) @@ -137,8 +139,9 @@ class CloudpipeController(object): @wsgi.serializers(xml=CloudpipesTemplate) def index(self, req): - """Show admins the list of running cloudpipe instances.""" + """List running cloudpipe instances.""" context = req.environ['nova.context'] + authorize(context) vpns = [] # TODO(todd): could use compute_api.get_all with admin context? for project in self.auth_manager.get_projects(): @@ -162,7 +165,6 @@ class Cloudpipe(extensions.ExtensionDescriptor): alias = "os-cloudpipe" namespace = "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1" updated = "2011-12-16T00:00:00+00:00" - admin_only = True def get_resources(self): resources = [] diff --git a/nova/api/openstack/compute/contrib/console_output.py b/nova/api/openstack/compute/contrib/console_output.py index ac578676e..04f975d5b 100644 --- a/nova/api/openstack/compute/contrib/console_output.py +++ b/nova/api/openstack/compute/contrib/console_output.py @@ -26,6 +26,7 @@ from nova.api.openstack import wsgi LOG = logging.getLogger('nova.api.openstack.compute.contrib.console_output') +authorize = extensions.extension_authorizer('compute', 'console_output') class ConsoleOutputController(wsgi.Controller): @@ -37,6 +38,7 @@ class ConsoleOutputController(wsgi.Controller): def get_console_output(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.routing_get(context, id) @@ -54,8 +56,6 @@ class ConsoleOutputController(wsgi.Controller): length) except exception.ApiError, e: raise webob.exc.HTTPBadRequest(explanation=e.message) - except exception.NotAuthorized, e: - raise webob.exc.HTTPUnauthorized() return {'output': output} diff --git a/nova/api/openstack/compute/contrib/consoles.py b/nova/api/openstack/compute/contrib/consoles.py index 46e3559ff..c3509e7a8 100644 --- a/nova/api/openstack/compute/contrib/consoles.py +++ b/nova/api/openstack/compute/contrib/consoles.py @@ -24,6 +24,7 @@ from nova.api.openstack import wsgi LOG = logging.getLogger('nova.api.openstack.compute.contrib.console') +authorize = extensions.extension_authorizer('compute', 'consoles') class ConsolesController(wsgi.Controller): @@ -35,6 +36,7 @@ class ConsolesController(wsgi.Controller): def get_vnc_console(self, req, id, body): """Get text console output.""" context = req.environ['nova.context'] + authorize(context) console_type = body['os-getVNCConsole'].get('type') diff --git a/nova/api/openstack/compute/contrib/createserverext.py b/nova/api/openstack/compute/contrib/createserverext.py index 05b0c7d9c..de4ba8dd0 100644 --- a/nova/api/openstack/compute/contrib/createserverext.py +++ b/nova/api/openstack/compute/contrib/createserverext.py @@ -19,13 +19,18 @@ from nova.api.openstack.compute import servers from nova.api.openstack.compute import views +authorize = extensions.soft_extension_authorizer('compute', 'createserverext') + + class ViewBuilder(views.servers.ViewBuilder): """Adds security group output when viewing server details.""" def show(self, request, instance): """Detailed view of a single instance.""" server = super(ViewBuilder, self).show(request, instance) - server["server"]["security_groups"] = self._get_groups(instance) + context = request.environ['nova.context'] + if authorize(context): + server["server"]["security_groups"] = self._get_groups(instance) return server def _get_groups(self, instance): diff --git a/nova/api/openstack/compute/contrib/deferred_delete.py b/nova/api/openstack/compute/contrib/deferred_delete.py index c0164eead..10f776558 100644 --- a/nova/api/openstack/compute/contrib/deferred_delete.py +++ b/nova/api/openstack/compute/contrib/deferred_delete.py @@ -26,6 +26,7 @@ from nova import log as logging LOG = logging.getLogger("nova.api.openstack.compute.contrib.deferred-delete") +authorize = extensions.extension_authorizer('compute', 'deferred_delete') class DeferredDeleteController(wsgi.Controller): @@ -36,8 +37,8 @@ class DeferredDeleteController(wsgi.Controller): @wsgi.action('restore') def _restore(self, req, id, body): """Restore a previously deleted instance.""" - context = req.environ["nova.context"] + authorize(context) instance = self.compute_api.get(context, id) try: self.compute_api.restore(context, instance) @@ -49,8 +50,8 @@ class DeferredDeleteController(wsgi.Controller): @wsgi.action('forceDelete') def _force_delete(self, req, id, body): """Force delete of instance before deferred cleanup.""" - context = req.environ["nova.context"] + authorize(context) instance = self.compute_api.get(context, id) try: self.compute_api.force_delete(context, instance) diff --git a/nova/api/openstack/compute/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py index cf78b847e..46c09ffa8 100644 --- a/nova/api/openstack/compute/contrib/disk_config.py +++ b/nova/api/openstack/compute/contrib/disk_config.py @@ -28,6 +28,7 @@ ALIAS = 'OS-DCF' XMLNS_DCF = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1" API_DISK_CONFIG = "%s:diskConfig" % ALIAS INTERNAL_DISK_CONFIG = "auto_disk_config" +authorize = extensions.soft_extension_authorizer('compute', 'disk_config') def disk_config_to_api(value): @@ -70,16 +71,16 @@ class ImageDiskConfigController(wsgi.Controller): @wsgi.extends def show(self, req, resp_obj, id): - if 'image' in resp_obj.obj: - context = req.environ['nova.context'] + context = req.environ['nova.context'] + if 'image' in resp_obj.obj and authorize(context): resp_obj.attach(xml=ImageDiskConfigTemplate()) image = resp_obj.obj['image'] self._add_disk_config(context, [image]) @wsgi.extends def detail(self, req, resp_obj): - if 'images' in resp_obj.obj: - context = req.environ['nova.context'] + context = req.environ['nova.context'] + if 'images' in resp_obj.obj and authorize(context): resp_obj.attach(xml=ImagesDiskConfigTemplate()) images = resp_obj.obj['images'] self._add_disk_config(context, images) @@ -102,13 +103,14 @@ class ServersDiskConfigTemplate(xmlutil.TemplateBuilder): class ServerDiskConfigController(wsgi.Controller): def _add_disk_config(self, context, servers): - # Filter out any servers that already have the key set (most likely - # from a remote zone) + # Filter out any servers that already have the key set + # (most likely from a remote zone) servers = [s for s in servers if API_DISK_CONFIG not in s] # Get DB information for servers uuids = [server['id'] for server in servers] - db_servers = db.instance_get_all_by_filters(context, {'uuid': uuids}) + db_servers = db.instance_get_all_by_filters(context, + {'uuid': uuids}) db_servers_by_uuid = dict((s['uuid'], s) for s in db_servers) for server in servers: @@ -117,21 +119,22 @@ class ServerDiskConfigController(wsgi.Controller): value = db_server[INTERNAL_DISK_CONFIG] server[API_DISK_CONFIG] = disk_config_to_api(value) - def _show(self, req, resp_obj): + def _show(self, context, resp_obj): if 'server' in resp_obj.obj: - context = req.environ['nova.context'] resp_obj.attach(xml=ServerDiskConfigTemplate()) server = resp_obj.obj['server'] self._add_disk_config(context, [server]) @wsgi.extends def show(self, req, resp_obj, id): - self._show(req, resp_obj) + context = req.environ['nova.context'] + if authorize(context): + self._show(context, resp_obj) @wsgi.extends def detail(self, req, resp_obj): - if 'servers' in resp_obj.obj: - context = req.environ['nova.context'] + context = req.environ['nova.context'] + if 'servers' in resp_obj.obj and authorize(context): resp_obj.attach(xml=ServersDiskConfigTemplate()) servers = resp_obj.obj['servers'] self._add_disk_config(context, servers) @@ -144,26 +147,34 @@ class ServerDiskConfigController(wsgi.Controller): @wsgi.extends def create(self, req, body): - self._set_disk_config(body['server']) - resp_obj = (yield) - self._show(req, resp_obj) + context = req.environ['nova.context'] + if authorize(context): + self._set_disk_config(body['server']) + resp_obj = (yield) + self._show(context, resp_obj) @wsgi.extends def update(self, req, id, body): - self._set_disk_config(body['server']) - resp_obj = (yield) - self._show(req, resp_obj) + context = req.environ['nova.context'] + if authorize(context): + self._set_disk_config(body['server']) + resp_obj = (yield) + self._show(context, resp_obj) @wsgi.extends(action='rebuild') def _action_rebuild(self, req, id, body): - self._set_disk_config(body['rebuild']) - resp_obj = (yield) - self._show(req, resp_obj) + context = req.environ['nova.context'] + if authorize(context): + self._set_disk_config(body['rebuild']) + resp_obj = (yield) + self._show(context, resp_obj) @wsgi.extends(action='resize') def _action_resize(self, req, id, body): - self._set_disk_config(body['resize']) - yield + context = req.environ['nova.context'] + if authorize(context): + self._set_disk_config(body['resize']) + yield class Disk_config(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/compute/contrib/extended_status.py b/nova/api/openstack/compute/contrib/extended_status.py index 7479d6f00..4ab7ca756 100644 --- a/nova/api/openstack/compute/contrib/extended_status.py +++ b/nova/api/openstack/compute/contrib/extended_status.py @@ -27,6 +27,7 @@ from nova import log as logging FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.openstack.compute.contrib.extendedstatus") +authorize = extensions.soft_extension_authorizer('compute', 'extended_status') class ExtendedStatusController(wsgi.Controller): @@ -48,34 +49,34 @@ class ExtendedStatusController(wsgi.Controller): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] + if authorize(context): + # Attach our slave template to the response object + resp_obj.attach(xml=ExtendedStatusTemplate()) - # Attach our slave template to the response object - resp_obj.attach(xml=ExtendedStatusTemplate()) - - try: - self._get_and_extend_one(context, id, resp_obj.obj['server']) - except exception.NotFound: - explanation = _("Server not found.") - raise exc.HTTPNotFound(explanation=explanation) + try: + self._get_and_extend_one(context, id, resp_obj.obj['server']) + except exception.NotFound: + explanation = _("Server not found.") + raise exc.HTTPNotFound(explanation=explanation) @wsgi.extends def detail(self, req, resp_obj): context = req.environ['nova.context'] - - # Attach our slave template to the response object - resp_obj.attach(xml=ExtendedStatusesTemplate()) - - for server in list(resp_obj.obj['servers']): - try: - self._get_and_extend_one(context, server['id'], server) - except exception.NotFound: - # NOTE(dtroyer): A NotFound exception at this point - # happens because a delete was in progress and the - # server that was present in the original call to - # compute.api.get_all() is no longer present. - # Delete it from the response and move on. - resp_obj.obj['servers'].remove(server) - continue + if authorize(context): + # Attach our slave template to the response object + resp_obj.attach(xml=ExtendedStatusesTemplate()) + + for server in list(resp_obj.obj['servers']): + try: + self._get_and_extend_one(context, server['id'], server) + except exception.NotFound: + # NOTE(dtroyer): A NotFound exception at this point + # happens because a delete was in progress and the + # server that was present in the original call to + # compute.api.get_all() is no longer present. + # Delete it from the response and move on. + resp_obj.obj['servers'].remove(server) + continue class Extended_status(extensions.ExtensionDescriptor): @@ -86,7 +87,6 @@ class Extended_status(extensions.ExtensionDescriptor): namespace = "http://docs.openstack.org/compute/ext/" \ "extended_status/api/v1.1" updated = "2011-11-03T00:00:00+00:00" - admin_only = True def get_controller_extensions(self): controller = ExtendedStatusController() diff --git a/nova/api/openstack/compute/contrib/flavorextraspecs.py b/nova/api/openstack/compute/contrib/flavorextraspecs.py index eafea5d1f..faf023659 100644 --- a/nova/api/openstack/compute/contrib/flavorextraspecs.py +++ b/nova/api/openstack/compute/contrib/flavorextraspecs.py @@ -26,6 +26,9 @@ from nova import db from nova import exception +authorize = extensions.extension_authorizer('compute', 'flavorextraspecs') + + class ExtraSpecsTemplate(xmlutil.TemplateBuilder): def construct(self): return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1) @@ -50,12 +53,14 @@ class FlavorExtraSpecsController(object): def index(self, req, flavor_id): """ Returns the list of extra specs for a givenflavor """ context = req.environ['nova.context'] + authorize(context) return self._get_extra_specs(context, flavor_id) @wsgi.serializers(xml=ExtraSpecsTemplate) def create(self, req, flavor_id, body): - self._check_body(body) context = req.environ['nova.context'] + authorize(context) + self._check_body(body) specs = body.get('extra_specs') try: db.instance_type_extra_specs_update_or_create(context, @@ -67,8 +72,9 @@ class FlavorExtraSpecsController(object): @wsgi.serializers(xml=ExtraSpecsTemplate) def update(self, req, flavor_id, id, body): - self._check_body(body) context = req.environ['nova.context'] + authorize(context) + self._check_body(body) if not id in body: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) @@ -88,6 +94,7 @@ class FlavorExtraSpecsController(object): def show(self, req, flavor_id, id): """ Return a single extra spec item """ context = req.environ['nova.context'] + authorize(context) specs = self._get_extra_specs(context, flavor_id) if id in specs['extra_specs']: return {id: specs['extra_specs'][id]} @@ -97,6 +104,7 @@ class FlavorExtraSpecsController(object): def delete(self, req, flavor_id, id): """ Deletes an existing extra spec """ context = req.environ['nova.context'] + authorize(context) db.instance_type_extra_specs_delete(context, flavor_id, id) def _handle_quota_error(self, error): diff --git a/nova/api/openstack/compute/contrib/floating_ip_dns.py b/nova/api/openstack/compute/contrib/floating_ip_dns.py index a0ac9241f..dbdd0c3a9 100644 --- a/nova/api/openstack/compute/contrib/floating_ip_dns.py +++ b/nova/api/openstack/compute/contrib/floating_ip_dns.py @@ -27,6 +27,7 @@ from nova import network LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_dns') +authorize = extensions.extension_authorizer('compute', 'floating_ip_dns') def make_dns_entry(elem): @@ -138,6 +139,7 @@ class FloatingIPDNSDomainController(object): def index(self, req): """Return a list of available DNS domains.""" context = req.environ['nova.context'] + authorize(context) domains = self.network_api.get_dns_domains(context) domainlist = [_create_domain_entry(domain['domain'], domain.get('scope'), @@ -151,6 +153,7 @@ class FloatingIPDNSDomainController(object): def update(self, req, id, body): """Add or modify domain entry""" context = req.environ['nova.context'] + authorize(context) fqdomain = _unquote_domain(id) try: entry = body['domain_entry'] @@ -185,6 +188,7 @@ class FloatingIPDNSDomainController(object): def delete(self, req, id): """Delete the domain identified by id. """ context = req.environ['nova.context'] + authorize(context) params = req.str_GET domain = _unquote_domain(id) @@ -210,6 +214,7 @@ class FloatingIPDNSEntryController(object): def show(self, req, domain_id, id): """Return the DNS entry that corresponds to domain_id and id.""" context = req.environ['nova.context'] + authorize(context) domain = _unquote_domain(domain_id) name = id @@ -222,6 +227,7 @@ class FloatingIPDNSEntryController(object): def index(self, req, domain_id): """Return a list of dns entries for the specified domain and ip.""" context = req.environ['nova.context'] + authorize(context) params = req.GET floating_ip = params.get('ip') domain = _unquote_domain(domain_id) @@ -241,6 +247,7 @@ class FloatingIPDNSEntryController(object): def update(self, req, domain_id, id, body): """Add or modify dns entry""" context = req.environ['nova.context'] + authorize(context) domain = _unquote_domain(domain_id) name = id try: @@ -268,6 +275,7 @@ class FloatingIPDNSEntryController(object): def delete(self, req, domain_id, id): """Delete the entry identified by req and id. """ context = req.environ['nova.context'] + authorize(context) domain = _unquote_domain(domain_id) name = id diff --git a/nova/api/openstack/compute/contrib/floating_ip_pools.py b/nova/api/openstack/compute/contrib/floating_ip_pools.py index 0bf2362af..574ef26b0 100644 --- a/nova/api/openstack/compute/contrib/floating_ip_pools.py +++ b/nova/api/openstack/compute/contrib/floating_ip_pools.py @@ -22,6 +22,7 @@ from nova import network LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_pools') +authorize = extensions.extension_authorizer('compute', 'floating_ip_pools') def _translate_floating_ip_view(pool): @@ -69,6 +70,7 @@ class FloatingIPPoolsController(object): def index(self, req): """Return a list of pools.""" context = req.environ['nova.context'] + authorize(context) pools = self.network_api.get_floating_ip_pools(context) return _translate_floating_ip_pools_view(pools) diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 3cfbc0604..afbdcf83a 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -30,6 +30,7 @@ from nova import rpc LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ips') +authorize = extensions.extension_authorizer('compute', 'floating_ips') def make_float_ip(elem): @@ -116,6 +117,7 @@ class FloatingIPController(object): def show(self, req, id): """Return data about the given floating ip.""" context = req.environ['nova.context'] + authorize(context) try: floating_ip = self.network_api.get_floating_ip(context, id) @@ -130,6 +132,7 @@ class FloatingIPController(object): def index(self, req): """Return a list of floating ips allocated to a project.""" context = req.environ['nova.context'] + authorize(context) floating_ips = self.network_api.get_floating_ips_by_project(context) @@ -141,6 +144,7 @@ class FloatingIPController(object): @wsgi.serializers(xml=FloatingIPTemplate) def create(self, req, body=None): context = req.environ['nova.context'] + authorize(context) pool = None if body and 'pool' in body: @@ -163,6 +167,7 @@ class FloatingIPController(object): def delete(self, req, id): context = req.environ['nova.context'] + authorize(context) floating_ip = self.network_api.get_floating_ip(context, id) if floating_ip.get('fixed_ip_id'): @@ -188,6 +193,7 @@ class FloatingIPActionController(wsgi.Controller): def _add_floating_ip(self, req, id, body): """Associate floating_ip to an instance.""" context = req.environ['nova.context'] + authorize(context) try: address = body['addFloatingIp']['address'] @@ -213,6 +219,7 @@ class FloatingIPActionController(wsgi.Controller): def _remove_floating_ip(self, req, id, body): """Dissociate floating_ip from an instance.""" context = req.environ['nova.context'] + authorize(context) try: address = body['removeFloatingIp']['address'] diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 66dd64def..6ee89859b 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -31,6 +31,7 @@ from nova.scheduler import api as scheduler_api LOG = logging.getLogger("nova.api.openstack.compute.contrib.hosts") FLAGS = flags.FLAGS +authorize = extensions.extension_authorizer('compute', 'hosts') class HostIndexTemplate(xmlutil.TemplateBuilder): @@ -112,12 +113,14 @@ class HostController(object): @wsgi.serializers(xml=HostIndexTemplate) def index(self, req): + authorize(req.environ['nova.context']) return {'hosts': _list_hosts(req)} @wsgi.serializers(xml=HostUpdateTemplate) @wsgi.deserializers(xml=HostDeserializer) @check_host def update(self, req, id, body): + authorize(req.environ['nova.context']) for raw_key, raw_val in body.iteritems(): key = raw_key.lower().strip() val = raw_val.lower().strip() @@ -149,6 +152,7 @@ class HostController(object): def _host_power_action(self, req, host, action): """Reboots, shuts down or powers up the host.""" context = req.environ['nova.context'] + authorize(context) try: result = self.compute_api.host_power_action(context, host=host, action=action) @@ -176,7 +180,6 @@ class Hosts(extensions.ExtensionDescriptor): alias = "os-hosts" namespace = "http://docs.openstack.org/compute/ext/hosts/api/v1.1" updated = "2011-06-29T00:00:00+00:00" - admin_only = True def get_resources(self): resources = [extensions.ResourceExtension('os-hosts', diff --git a/nova/api/openstack/compute/contrib/keypairs.py b/nova/api/openstack/compute/contrib/keypairs.py index 504539d55..7cd919673 100644 --- a/nova/api/openstack/compute/contrib/keypairs.py +++ b/nova/api/openstack/compute/contrib/keypairs.py @@ -31,6 +31,9 @@ from nova import db from nova import exception +authorize = extensions.extension_authorizer('compute', 'keypairs') + + class KeypairTemplate(xmlutil.TemplateBuilder): def construct(self): return xmlutil.MasterTemplate(xmlutil.make_flat_dict('keypair'), 1) @@ -77,6 +80,7 @@ class KeypairController(object): """ context = req.environ['nova.context'] + authorize(context) params = body['keypair'] name = params['name'] @@ -109,6 +113,7 @@ class KeypairController(object): Delete a keypair with a given name """ context = req.environ['nova.context'] + authorize(context) db.key_pair_destroy(context, context.user_id, id) return webob.Response(status_int=202) @@ -118,6 +123,7 @@ class KeypairController(object): List of keypairs for a user """ context = req.environ['nova.context'] + authorize(context) key_pairs = db.key_pair_get_all_by_user(context, context.user_id) rval = [] for key_pair in key_pairs: diff --git a/nova/api/openstack/compute/contrib/multinic.py b/nova/api/openstack/compute/contrib/multinic.py index eea4532df..a3b4a8dc8 100644 --- a/nova/api/openstack/compute/contrib/multinic.py +++ b/nova/api/openstack/compute/contrib/multinic.py @@ -26,6 +26,7 @@ from nova import log as logging LOG = logging.getLogger("nova.api.openstack.compute.contrib.multinic") +authorize = extensions.extension_authorizer('compute', 'multinic') class MultinicController(wsgi.Controller): @@ -43,13 +44,14 @@ class MultinicController(wsgi.Controller): @wsgi.action('addFixedIp') def _add_fixed_ip(self, req, id, body): """Adds an IP on a given network to an instance.""" + context = req.environ['nova.context'] + authorize(context) # Validate the input entity if 'networkId' not in body['addFixedIp']: msg = _("Missing 'networkId' argument for addFixedIp") raise exc.HTTPUnprocessableEntity(explanation=msg) - context = req.environ['nova.context'] instance = self._get_instance(context, id) network_id = body['addFixedIp']['networkId'] self.compute_api.add_fixed_ip(context, instance, network_id) @@ -58,13 +60,14 @@ class MultinicController(wsgi.Controller): @wsgi.action('removeFixedIp') def _remove_fixed_ip(self, req, id, body): """Removes an IP from an instance.""" + context = req.environ['nova.context'] + authorize(context) # Validate the input entity if 'address' not in body['removeFixedIp']: msg = _("Missing 'address' argument for removeFixedIp") raise exc.HTTPUnprocessableEntity(explanation=msg) - context = req.environ['nova.context'] instance = self._get_instance(context, id) address = body['removeFixedIp']['address'] diff --git a/nova/api/openstack/compute/contrib/networks.py b/nova/api/openstack/compute/contrib/networks.py index f2381a19d..765a1d2fe 100644 --- a/nova/api/openstack/compute/contrib/networks.py +++ b/nova/api/openstack/compute/contrib/networks.py @@ -28,6 +28,7 @@ import nova.network.api FLAGS = flags.FLAGS LOG = logging.getLogger('nova.api.openstack.compute.contrib.networks') +authorize = extensions.extension_authorizer('compute', 'networks') def network_dict(network): @@ -65,6 +66,7 @@ class NetworkController(object): def _disassociate(self, request, network_id, body): context = request.environ['nova.context'] + authorize(context) LOG.debug(_("Disassociating network with id %s" % network_id)) try: self.network_api.disassociate(context, network_id) @@ -74,12 +76,14 @@ class NetworkController(object): def index(self, req): context = req.environ['nova.context'] + authorize(context) networks = self.network_api.get_all(context) result = [network_dict(net_ref) for net_ref in networks] return {'networks': result} def show(self, req, id): context = req.environ['nova.context'] + authorize(context) LOG.debug(_("Showing network with id %s") % id) try: network = self.network_api.get(context, id) @@ -89,6 +93,7 @@ class NetworkController(object): def delete(self, req, id): context = req.environ['nova.context'] + authorize(context) LOG.info(_("Deleting network with id %s") % id) try: self.network_api.delete(context, id) @@ -107,7 +112,6 @@ class Networks(extensions.ExtensionDescriptor): alias = "os-networks" namespace = "http://docs.openstack.org/compute/ext/networks/api/v1.1" updated = "2011-12-23 00:00:00" - admin_only = True def get_resources(self): member_actions = {'action': 'POST'} diff --git a/nova/api/openstack/compute/contrib/quotas.py b/nova/api/openstack/compute/contrib/quotas.py index 5e4a20568..ce466af3f 100644 --- a/nova/api/openstack/compute/contrib/quotas.py +++ b/nova/api/openstack/compute/contrib/quotas.py @@ -25,6 +25,9 @@ from nova import exception from nova import quota +authorize = extensions.extension_authorizer('compute', 'quotas') + + quota_resources = ['metadata_items', 'injected_file_content_bytes', 'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances', 'injected_files', 'cores'] @@ -57,6 +60,7 @@ class QuotaSetsController(object): @wsgi.serializers(xml=QuotaTemplate) def show(self, req, id): context = req.environ['nova.context'] + authorize(context) try: db.sqlalchemy.api.authorize_project_context(context, id) return self._format_quota_set(id, @@ -67,6 +71,7 @@ class QuotaSetsController(object): @wsgi.serializers(xml=QuotaTemplate) def update(self, req, id, body): context = req.environ['nova.context'] + authorize(context) project_id = id for key in body['quota_set'].keys(): if key in quota_resources: @@ -80,6 +85,7 @@ class QuotaSetsController(object): return {'quota_set': quota.get_project_quotas(context, project_id)} def defaults(self, req, id): + authorize(req.environ['nova.context']) return self._format_quota_set(id, quota._get_default_quotas()) diff --git a/nova/api/openstack/compute/contrib/rescue.py b/nova/api/openstack/compute/contrib/rescue.py index b0d5c6f97..e5fbb8388 100644 --- a/nova/api/openstack/compute/contrib/rescue.py +++ b/nova/api/openstack/compute/contrib/rescue.py @@ -28,6 +28,7 @@ from nova import utils FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.openstack.compute.contrib.rescue") +authorize = exts.extension_authorizer('compute', 'rescue') class RescueController(wsgi.Controller): @@ -47,6 +48,7 @@ class RescueController(wsgi.Controller): def _rescue(self, req, id, body): """Rescue an instance.""" context = req.environ["nova.context"] + authorize(context) if body['rescue'] and 'adminPass' in body['rescue']: password = body['rescue']['adminPass'] @@ -62,6 +64,7 @@ class RescueController(wsgi.Controller): def _unrescue(self, req, id, body): """Unrescue an instance.""" context = req.environ["nova.context"] + authorize(context) instance = self._get_instance(context, id) self.compute_api.unrescue(context, instance) return webob.Response(status_int=202) diff --git a/nova/api/openstack/compute/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index 23b741145..45d72bf61 100644 --- a/nova/api/openstack/compute/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -35,6 +35,7 @@ from nova import utils LOG = logging.getLogger("nova.api.openstack.compute.contrib.security_groups") FLAGS = flags.FLAGS +authorize = extensions.extension_authorizer('compute', 'security_groups') def make_rule(elem): @@ -223,6 +224,7 @@ class SecurityGroupController(object): def show(self, req, id): """Return data about the given security group.""" context = req.environ['nova.context'] + authorize(context) security_group = self._get_security_group(context, id) return {'security_group': self._format_security_group(context, security_group)} @@ -230,6 +232,7 @@ class SecurityGroupController(object): def delete(self, req, id): """Delete a security group.""" context = req.environ['nova.context'] + authorize(context) security_group = self._get_security_group(context, id) LOG.audit(_("Delete security group %s"), id, context=context) db.security_group_destroy(context, security_group.id) @@ -240,6 +243,7 @@ class SecurityGroupController(object): def index(self, req): """Returns a list of security groups""" context = req.environ['nova.context'] + authorize(context) self.compute_api.ensure_default_security_group(context) groups = db.security_group_get_by_project(context, @@ -257,6 +261,7 @@ class SecurityGroupController(object): def create(self, req, body): """Creates a new security group.""" context = req.environ['nova.context'] + authorize(context) if not body: raise exc.HTTPUnprocessableEntity() @@ -313,6 +318,7 @@ class SecurityGroupRulesController(SecurityGroupController): @wsgi.deserializers(xml=SecurityGroupRulesXMLDeserializer) def create(self, req, body): context = req.environ['nova.context'] + authorize(context) if not body: raise exc.HTTPUnprocessableEntity() @@ -471,6 +477,7 @@ class SecurityGroupRulesController(SecurityGroupController): def delete(self, req, id): context = req.environ['nova.context'] + authorize(context) self.compute_api.ensure_default_security_group(context) try: @@ -505,6 +512,7 @@ class SecurityGroupActionController(wsgi.Controller): @wsgi.action('addSecurityGroup') def _addSecurityGroup(self, req, id, body): context = req.environ['nova.context'] + authorize(context) try: body = body['addSecurityGroup'] @@ -535,6 +543,7 @@ class SecurityGroupActionController(wsgi.Controller): @wsgi.action('removeSecurityGroup') def _removeSecurityGroup(self, req, id, body): context = req.environ['nova.context'] + authorize(context) try: body = body['removeSecurityGroup'] diff --git a/nova/api/openstack/compute/contrib/server_action_list.py b/nova/api/openstack/compute/contrib/server_action_list.py index 436758572..24d3595d4 100644 --- a/nova/api/openstack/compute/contrib/server_action_list.py +++ b/nova/api/openstack/compute/contrib/server_action_list.py @@ -23,6 +23,7 @@ from nova import exception sa_nsmap = {None: wsgi.XMLNS_V11} +authorize = extensions.extension_authorizer('compute', 'server_action_list') class ServerActionsTemplate(xmlutil.TemplateBuilder): @@ -39,6 +40,7 @@ class ServerActionListController(object): @wsgi.serializers(xml=ServerActionsTemplate) def index(self, req, server_id): context = req.environ["nova.context"] + authorize(context) compute_api = compute.API() try: @@ -66,7 +68,6 @@ class Server_action_list(extensions.ExtensionDescriptor): namespace = "http://docs.openstack.org/compute/ext/" \ "server-actions-list/api/v1.1" updated = "2011-12-21T00:00:00+00:00" - admin_only = True def get_resources(self): parent_def = {'member_name': 'server', 'collection_name': 'servers'} diff --git a/nova/api/openstack/compute/contrib/server_diagnostics.py b/nova/api/openstack/compute/contrib/server_diagnostics.py index 11d1affaf..49afcac01 100644 --- a/nova/api/openstack/compute/contrib/server_diagnostics.py +++ b/nova/api/openstack/compute/contrib/server_diagnostics.py @@ -23,6 +23,7 @@ from nova import exception from nova.scheduler import api as scheduler_api +authorize = extensions.extension_authorizer('compute', 'server_diagnostics') sd_nsmap = {None: wsgi.XMLNS_V11} @@ -41,6 +42,7 @@ class ServerDiagnosticsController(object): @scheduler_api.redirect_handler def index(self, req, server_id): context = req.environ["nova.context"] + authorize(context) compute_api = compute.API() try: instance = compute_api.get(context, id) @@ -58,7 +60,6 @@ class Server_diagnostics(extensions.ExtensionDescriptor): namespace = "http://docs.openstack.org/compute/ext/" \ "server-diagnostics/api/v1.1" updated = "2011-12-21T00:00:00+00:00" - admin_only = True def get_resources(self): parent_def = {'member_name': 'server', 'collection_name': 'servers'} diff --git a/nova/api/openstack/compute/contrib/simple_tenant_usage.py b/nova/api/openstack/compute/contrib/simple_tenant_usage.py index 85ea9a859..8e4201546 100644 --- a/nova/api/openstack/compute/contrib/simple_tenant_usage.py +++ b/nova/api/openstack/compute/contrib/simple_tenant_usage.py @@ -29,6 +29,7 @@ from nova import flags FLAGS = flags.FLAGS +authorize = extensions.extension_authorizer('compute', 'simple_tenant_usage') def make_usage(elem): @@ -211,6 +212,7 @@ class SimpleTenantUsageController(object): def index(self, req): """Retrive tenant_usage for all tenants""" context = req.environ['nova.context'] + authorize(context) if not context.is_admin: return webob.Response(status_int=403) @@ -227,6 +229,7 @@ class SimpleTenantUsageController(object): """Retrive tenant_usage for a specified tenant""" tenant_id = id context = req.environ['nova.context'] + authorize(context) if not context.is_admin: if tenant_id != context.project_id: @@ -253,7 +256,6 @@ class Simple_tenant_usage(extensions.ExtensionDescriptor): namespace = "http://docs.openstack.org/compute/ext/" \ "os-simple-tenant-usage/api/v1.1" updated = "2011-08-19T00:00:00+00:00" - admin_only = True def get_resources(self): resources = [] diff --git a/nova/api/openstack/compute/contrib/users.py b/nova/api/openstack/compute/contrib/users.py index 55dba02e4..b2a97f327 100644 --- a/nova/api/openstack/compute/contrib/users.py +++ b/nova/api/openstack/compute/contrib/users.py @@ -27,6 +27,7 @@ from nova import log as logging FLAGS = flags.FLAGS LOG = logging.getLogger('nova.api.openstack.users') +authorize = extensions.extension_authorizer('compute', 'users') def make_user(elem): @@ -65,15 +66,10 @@ class Controller(object): def __init__(self): self.manager = manager.AuthManager() - def _check_admin(self, context): - """We cannot depend on the db layer to check for admin access - for the auth manager, so we do it here""" - if not context.is_admin: - raise exception.AdminRequired() - @wsgi.serializers(xml=UsersTemplate) def index(self, req): """Return all users in brief""" + authorize(req.environ['nova.context']) users = self.manager.get_users() users = common.limited(users, req) users = [_translate_keys(user) for user in users] @@ -87,6 +83,7 @@ class Controller(object): @wsgi.serializers(xml=UserTemplate) def show(self, req, id): """Return data about the given user id""" + authorize(req.environ['nova.context']) #NOTE(justinsb): The drivers are a little inconsistent in how they # deal with "NotFound" - some throw, some return None. @@ -101,13 +98,13 @@ class Controller(object): return dict(user=_translate_keys(user)) def delete(self, req, id): - self._check_admin(req.environ['nova.context']) + authorize(req.environ['nova.context']) self.manager.delete_user(id) return {} @wsgi.serializers(xml=UserTemplate) def create(self, req, body): - self._check_admin(req.environ['nova.context']) + authorize(req.environ['nova.context']) is_admin = body['user'].get('admin') in ('T', 'True', True) name = body['user'].get('name') access = body['user'].get('access') @@ -117,7 +114,7 @@ class Controller(object): @wsgi.serializers(xml=UserTemplate) def update(self, req, id, body): - self._check_admin(req.environ['nova.context']) + authorize(req.environ['nova.context']) is_admin = body['user'].get('admin') if is_admin is not None: is_admin = is_admin in ('T', 'True', True) @@ -134,7 +131,6 @@ class Users(extensions.ExtensionDescriptor): alias = "os-users" namespace = "http://docs.openstack.org/compute/ext/users/api/v1.1" updated = "2011-08-08T00:00:00+00:00" - admin_only = True def get_resources(self): coll_actions = {'detail': 'GET'} diff --git a/nova/api/openstack/compute/contrib/virtual_interfaces.py b/nova/api/openstack/compute/contrib/virtual_interfaces.py index ea37c4d97..e8da23c00 100644 --- a/nova/api/openstack/compute/contrib/virtual_interfaces.py +++ b/nova/api/openstack/compute/contrib/virtual_interfaces.py @@ -25,6 +25,7 @@ from nova import network LOG = logging.getLogger("nova.api.openstack.compute." "contrib.virtual_interfaces") +authorize = extensions.extension_authorizer('compute', 'virtual_interfaces') vif_nsmap = {None: wsgi.XMLNS_V11} @@ -68,6 +69,7 @@ class ServerVirtualInterfaceController(object): @wsgi.serializers(xml=VirtualInterfaceTemplate) def index(self, req, server_id): """Returns the list of VIFs for a given instance.""" + authorize(req.environ['nova.context']) return self._items(req, server_id, entity_maker=_translate_vif_summary_view) diff --git a/nova/api/openstack/compute/contrib/virtual_storage_arrays.py b/nova/api/openstack/compute/contrib/virtual_storage_arrays.py index 10e8a1001..ed4b12a92 100644 --- a/nova/api/openstack/compute/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/compute/contrib/virtual_storage_arrays.py @@ -36,9 +36,11 @@ from nova import log as logging from nova import vsa from nova import volume -FLAGS = flags.FLAGS +FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.openstack.compute.contrib.vsa") +authorize = extensions.extension_authorizer('compute', + 'virtual_storage_arrays') def _vsa_view(context, vsa, details=False, instances=None): @@ -124,6 +126,7 @@ class VsaController(object): def _items(self, req, details): """Return summary or detailed list of VSAs.""" context = req.environ['nova.context'] + authorize(context) vsas = self.vsa_api.get_all(context) limited_list = common.limited(vsas, req) @@ -147,6 +150,7 @@ class VsaController(object): def show(self, req, id): """Return data about the given VSA.""" context = req.environ['nova.context'] + authorize(context) try: vsa = self.vsa_api.get(context, vsa_id=id) @@ -160,6 +164,7 @@ class VsaController(object): def create(self, req, body): """Create a new VSA.""" context = req.environ['nova.context'] + authorize(context) if not body or 'vsa' not in body: LOG.debug(_("No body provided"), context=context) @@ -193,6 +198,7 @@ class VsaController(object): def delete(self, req, id): """Delete a VSA.""" context = req.environ['nova.context'] + authorize(context) LOG.audit(_("Delete VSA with id: %s"), id, context=context) @@ -206,6 +212,7 @@ class VsaController(object): auto or manually associate an IP to VSA """ context = req.environ['nova.context'] + authorize(context) if body is None: ip = 'auto' @@ -234,6 +241,7 @@ class VsaController(object): auto or manually associate an IP to VSA """ context = req.environ['nova.context'] + authorize(context) if body is None: ip = 'auto' @@ -293,6 +301,7 @@ class VsaVolumeDriveController(volumes.VolumeController): def _items(self, req, vsa_id, details): """Return summary or detailed list of volumes for particular VSA.""" context = req.environ['nova.context'] + authorize(context) vols = self.volume_api.get_all(context, search_opts={'metadata': {self.direction: str(vsa_id)}}) @@ -317,6 +326,7 @@ class VsaVolumeDriveController(volumes.VolumeController): """Create a new volume from VSA.""" LOG.audit(_("Create. vsa_id=%(vsa_id)s, body=%(body)s"), locals()) context = req.environ['nova.context'] + authorize(context) if not body: raise exc.HTTPUnprocessableEntity() @@ -345,6 +355,7 @@ class VsaVolumeDriveController(volumes.VolumeController): def update(self, req, vsa_id, id, body): """Update a volume.""" context = req.environ['nova.context'] + authorize(context) try: self._check_volume_ownership(context, vsa_id, id) @@ -380,6 +391,7 @@ class VsaVolumeDriveController(volumes.VolumeController): def delete(self, req, vsa_id, id): """Delete a volume.""" context = req.environ['nova.context'] + authorize(context) LOG.audit(_("Delete. vsa_id=%(vsa_id)s, id=%(id)s"), locals()) @@ -395,6 +407,7 @@ class VsaVolumeDriveController(volumes.VolumeController): def show(self, req, vsa_id, id): """Return data about the given volume.""" context = req.environ['nova.context'] + authorize(context) LOG.audit(_("Show. vsa_id=%(vsa_id)s, id=%(id)s"), locals()) diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py index 83e67102b..c3ba33eef 100644 --- a/nova/api/openstack/compute/contrib/volumes.py +++ b/nova/api/openstack/compute/contrib/volumes.py @@ -32,9 +32,8 @@ from nova.volume import volume_types LOG = logging.getLogger("nova.api.openstack.compute.contrib.volumes") - - FLAGS = flags.FLAGS +authorize = extensions.extension_authorizer('compute', 'volumes') def _translate_volume_detail_view(context, vol): @@ -130,6 +129,7 @@ class VolumeController(object): def show(self, req, id): """Return data about the given volume.""" context = req.environ['nova.context'] + authorize(context) try: vol = self.volume_api.get(context, id) @@ -141,6 +141,7 @@ class VolumeController(object): def delete(self, req, id): """Delete a volume.""" context = req.environ['nova.context'] + authorize(context) LOG.audit(_("Delete volume with id: %s"), id, context=context) @@ -164,6 +165,7 @@ class VolumeController(object): def _items(self, req, entity_maker): """Returns a list of volumes, transformed through entity_maker.""" context = req.environ['nova.context'] + authorize(context) volumes = self.volume_api.get_all(context) limited_list = common.limited(volumes, req) @@ -174,6 +176,7 @@ class VolumeController(object): def create(self, req, body): """Creates a new volume.""" context = req.environ['nova.context'] + authorize(context) if not body: raise exc.HTTPUnprocessableEntity() @@ -289,6 +292,7 @@ class VolumeAttachmentController(object): def show(self, req, server_id, id): """Return data about the given volume attachment.""" context = req.environ['nova.context'] + authorize(context) volume_id = id try: @@ -309,6 +313,7 @@ class VolumeAttachmentController(object): def create(self, req, server_id, body): """Attach a volume to an instance.""" context = req.environ['nova.context'] + authorize(context) if not body: raise exc.HTTPUnprocessableEntity() @@ -350,6 +355,7 @@ class VolumeAttachmentController(object): def delete(self, req, server_id, id): """Detach a volume from an instance.""" context = req.environ['nova.context'] + authorize(context) volume_id = id LOG.audit(_("Detach volume %s"), volume_id, context=context) @@ -372,6 +378,7 @@ class VolumeAttachmentController(object): def _items(self, req, server_id, entity_maker): """Returns a list of attachments, transformed through entity_maker.""" context = req.environ['nova.context'] + authorize(context) try: instance = self.compute_api.get(context, server_id) @@ -452,6 +459,7 @@ class SnapshotController(object): def show(self, req, id): """Return data about the given snapshot.""" context = req.environ['nova.context'] + authorize(context) try: vol = self.volume_api.get_snapshot(context, id) @@ -463,6 +471,7 @@ class SnapshotController(object): def delete(self, req, id): """Delete a snapshot.""" context = req.environ['nova.context'] + authorize(context) LOG.audit(_("Delete snapshot with id: %s"), id, context=context) @@ -485,6 +494,7 @@ class SnapshotController(object): def _items(self, req, entity_maker): """Returns a list of snapshots, transformed through entity_maker.""" context = req.environ['nova.context'] + authorize(context) snapshots = self.volume_api.get_all_snapshots(context) limited_list = common.limited(snapshots, req) @@ -495,6 +505,7 @@ class SnapshotController(object): def create(self, req, body): """Creates a new snapshot.""" context = req.environ['nova.context'] + authorize(context) if not body: return exc.HTTPUnprocessableEntity() diff --git a/nova/api/openstack/compute/contrib/volumetypes.py b/nova/api/openstack/compute/contrib/volumetypes.py index bf249f3f8..cbc205ea7 100644 --- a/nova/api/openstack/compute/contrib/volumetypes.py +++ b/nova/api/openstack/compute/contrib/volumetypes.py @@ -27,6 +27,9 @@ from nova import exception from nova.volume import volume_types +authorize = extensions.extension_authorizer('compute', 'volumetypes') + + def make_voltype(elem): elem.set('id') elem.set('name') @@ -57,12 +60,14 @@ class VolumeTypesController(object): def index(self, req): """ Returns the list of volume types """ context = req.environ['nova.context'] + authorize(context) return volume_types.get_all_types(context) @wsgi.serializers(xml=VolumeTypeTemplate) def create(self, req, body): """Creates a new volume type.""" context = req.environ['nova.context'] + authorize(context) if not body or body == "": raise exc.HTTPUnprocessableEntity() @@ -91,6 +96,7 @@ class VolumeTypesController(object): def show(self, req, id): """ Return a single volume type item """ context = req.environ['nova.context'] + authorize(context) try: vol_type = volume_types.get_volume_type(context, id) @@ -102,6 +108,7 @@ class VolumeTypesController(object): def delete(self, req, id): """ Deletes an existing volume type """ context = req.environ['nova.context'] + authorize(context) try: vol_type = volume_types.get_volume_type(context, id) @@ -155,12 +162,14 @@ class VolumeTypeExtraSpecsController(object): def index(self, req, vol_type_id): """ Returns the list of extra specs for a given volume type """ context = req.environ['nova.context'] + authorize(context) return self._get_extra_specs(context, vol_type_id) @wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate) def create(self, req, vol_type_id, body): - self._check_body(body) context = req.environ['nova.context'] + authorize(context) + self._check_body(body) specs = body.get('extra_specs') try: db.volume_type_extra_specs_update_or_create(context, @@ -172,8 +181,9 @@ class VolumeTypeExtraSpecsController(object): @wsgi.serializers(xml=VolumeTypeExtraSpecTemplate) def update(self, req, vol_type_id, id, body): - self._check_body(body) context = req.environ['nova.context'] + authorize(context) + self._check_body(body) if not id in body: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) @@ -193,6 +203,7 @@ class VolumeTypeExtraSpecsController(object): def show(self, req, vol_type_id, id): """ Return a single extra spec item """ context = req.environ['nova.context'] + authorize(context) specs = self._get_extra_specs(context, vol_type_id) if id in specs['extra_specs']: return {id: specs['extra_specs'][id]} @@ -202,6 +213,7 @@ class VolumeTypeExtraSpecsController(object): def delete(self, req, vol_type_id, id): """ Deletes an existing extra spec """ context = req.environ['nova.context'] + authorize(context) db.volume_type_extra_specs_delete(context, vol_type_id, id) def _handle_quota_error(self, error): diff --git a/nova/api/openstack/compute/contrib/zones.py b/nova/api/openstack/compute/contrib/zones.py index 539833ee2..fac3d7188 100644 --- a/nova/api/openstack/compute/contrib/zones.py +++ b/nova/api/openstack/compute/contrib/zones.py @@ -34,6 +34,7 @@ import nova.scheduler.api LOG = logging.getLogger("nova.api.openstack.compute.contrib.zones") FLAGS = flags.FLAGS +authorize = extensions.extension_authorizer('compute', 'zones') class CapabilitySelector(object): @@ -117,6 +118,7 @@ class Controller(object): @wsgi.serializers(xml=ZonesTemplate) def index(self, req): """Return all zones in brief""" + authorize(req.environ['nova.context']) # Ask the ZoneManager in the Scheduler for most recent data, # or fall-back to the database ... items = nova.scheduler.api.get_zone_list(req.environ['nova.context']) @@ -133,6 +135,7 @@ class Controller(object): def info(self, req): """Return name and capabilities for this zone.""" context = req.environ['nova.context'] + authorize(context) zone_capabs = nova.scheduler.api.get_zone_capabilities(context) # NOTE(comstud): This should probably return, instead: # {'zone': {'name': FLAGS.zone_name, @@ -143,13 +146,15 @@ class Controller(object): @wsgi.serializers(xml=ZoneTemplate) def show(self, req, id): """Return data about the given zone id""" - zone_id = int(id) context = req.environ['nova.context'] + authorize(context) + zone_id = int(id) zone = nova.scheduler.api.zone_get(context, zone_id) return dict(zone=_scrub_zone(zone)) def delete(self, req, id): """Delete a child zone entry.""" + authorize(req.environ['nova.context']) zone_id = int(id) nova.scheduler.api.zone_delete(req.environ['nova.context'], zone_id) return {} @@ -159,6 +164,7 @@ class Controller(object): def create(self, req, body): """Create a child zone entry.""" context = req.environ['nova.context'] + authorize(context) zone = nova.scheduler.api.zone_create(context, body["zone"]) return dict(zone=_scrub_zone(zone)) @@ -166,6 +172,7 @@ class Controller(object): def update(self, req, id, body): """Update a child zone entry.""" context = req.environ['nova.context'] + authorize(context) zone_id = int(id) zone = nova.scheduler.api.zone_update(context, zone_id, body["zone"]) return dict(zone=_scrub_zone(zone)) @@ -175,9 +182,10 @@ class Controller(object): def select(self, req, body): """Returns a weighted list of costs to create instances of desired capabilities.""" - ctx = req.environ['nova.context'] + context = req.environ['nova.context'] + authorize(context) specs = json.loads(body) - build_plan = nova.scheduler.api.select(ctx, specs=specs) + build_plan = nova.scheduler.api.select(context, specs=specs) cooked = self._scrub_build_plan(build_plan) return {"weights": cooked} @@ -205,7 +213,6 @@ class Zones(extensions.ExtensionDescriptor): alias = "os-zones" namespace = "http://docs.openstack.org/compute/ext/zones/api/v1.1" updated = "2011-09-21T00:00:00+00:00" - admin_only = True def get_resources(self): #NOTE(bcwaldon): This resource should be prefixed with 'os-' diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index ee7038c6e..d0e771e22 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -1170,7 +1170,7 @@ def create_resource(): def remove_invalid_options(context, search_options, allowed_search_options): """Remove search options that are not valid for non-admin API/context""" - if FLAGS.allow_admin_api and context.is_admin: + if context.is_admin: # Allow all options return # Otherwise, strip out all unknown options diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 4b8da21ed..bf415765c 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -16,8 +16,10 @@ # License for the specific language governing permissions and limitations # under the License. +import functools import os import routes + import webob.dec import webob.exc @@ -27,13 +29,12 @@ from nova.api.openstack import xmlutil from nova import exception from nova import flags from nova import log as logging +import nova.policy from nova import utils from nova import wsgi as base_wsgi LOG = logging.getLogger('nova.api.openstack.extensions') - - FLAGS = flags.FLAGS @@ -61,10 +62,6 @@ class ExtensionDescriptor(object): # '2011-01-22T13:25:27-06:00' updated = None - # This attribute causes the extension to load only when - # the admin api is enabled - admin_only = False - def __init__(self, ext_mgr): """Register extension with the extension manager.""" @@ -246,15 +243,10 @@ class ExtensionManager(object): ' '.join(extension.__doc__.strip().split())) LOG.debug(_('Ext namespace: %s'), extension.namespace) LOG.debug(_('Ext updated: %s'), extension.updated) - LOG.debug(_('Ext admin_only: %s'), extension.admin_only) except AttributeError as ex: LOG.exception(_("Exception loading extension: %s"), unicode(ex)) return False - # Don't load admin api extensions if the admin api isn't enabled - if not FLAGS.allow_admin_api and extension.admin_only: - return False - return True def load_extension(self, ext_factory): @@ -384,3 +376,22 @@ def load_standard_extensions(ext_mgr, logger, path, package): # Update the list of directories we'll explore... dirnames[:] = subdirs + + +def extension_authorizer(api_name, extension_name): + def authorize(context): + action = '%s_extension:%s' % (api_name, extension_name) + nova.policy.enforce(context, action, {}) + return authorize + + +def soft_extension_authorizer(api_name, extension_name): + hard_authorize = extension_authorizer(api_name, extension_name) + + def authorize(context): + try: + hard_authorize(context) + return True + except exception.NotAuthorized: + return False + return authorize |
