13f9e02a64
Blueprint make-authz-orthogonal This patch implements part #3 of this blueprint, according to its specification. It does so by allowing the view generator in the API layer to strip off fields which do not satify authorization policies. Also, some checks in unit tests for plugins relied on the capability of the plugin to invoke directly the policy engine. This checks have been removed and replaced by equivalent unit tests. Finally, this patch required changes to most test cases for API extensions in order to ensure the resource attribute map was updated with the extension's attributes Change-Id: I1ef94a8a628d34697254b68d7a539bd1c636876e
212 lines
8.6 KiB
Python
212 lines
8.6 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2013 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
# @author: Salvatore Orlando, VMware
|
|
#
|
|
|
|
from quantum.api import extensions
|
|
from quantum.api.v2 import attributes
|
|
from quantum.api.v2 import base
|
|
from quantum import context
|
|
from quantum.db import servicetype_db
|
|
from quantum import manager
|
|
from quantum.openstack.common import log as logging
|
|
from quantum.plugins.common import constants
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
RESOURCE_NAME = "service_type"
|
|
COLLECTION_NAME = "%ss" % RESOURCE_NAME
|
|
SERVICE_ATTR = 'service_class'
|
|
PLUGIN_ATTR = 'plugin'
|
|
DRIVER_ATTR = 'driver'
|
|
EXT_ALIAS = 'service-type'
|
|
|
|
# Attribute Map for Service Type Resource
|
|
RESOURCE_ATTRIBUTE_MAP = {
|
|
COLLECTION_NAME: {
|
|
'id': {'allow_post': False, 'allow_put': False,
|
|
'is_visible': True},
|
|
'name': {'allow_post': True, 'allow_put': True,
|
|
'validate': {'type:string': None},
|
|
'is_visible': True, 'default': ''},
|
|
'default': {'allow_post': False, 'allow_put': False,
|
|
'is_visible': True},
|
|
#TODO(salvatore-orlando): Service types should not have ownership
|
|
'tenant_id': {'allow_post': True, 'allow_put': False,
|
|
'required_by_policy': True,
|
|
'is_visible': True},
|
|
'num_instances': {'allow_post': False, 'allow_put': False,
|
|
'is_visible': True},
|
|
'service_definitions': {'allow_post': True, 'allow_put': True,
|
|
'is_visible': True, 'default': None,
|
|
'validate': {'type:service_definitions':
|
|
None}}
|
|
}
|
|
}
|
|
|
|
|
|
def set_default_svctype_id(original_id):
|
|
if not original_id:
|
|
svctype_mgr = servicetype_db.ServiceTypeManager.get_instance()
|
|
# Fetch default service type - it must exist
|
|
res = svctype_mgr.get_service_types(context.get_admin_context(),
|
|
filters={'default': [True]})
|
|
return res[0]['id']
|
|
return original_id
|
|
|
|
|
|
def _validate_servicetype_ref(data, valid_values=None):
|
|
"""Verify the service type id exists."""
|
|
svc_type_id = data
|
|
svctype_mgr = servicetype_db.ServiceTypeManager.get_instance()
|
|
try:
|
|
svctype_mgr.get_service_type(context.get_admin_context(),
|
|
svc_type_id)
|
|
except servicetype_db.ServiceTypeNotFound:
|
|
return _("The service type '%s' does not exist") % svc_type_id
|
|
|
|
|
|
def _validate_service_defs(data, valid_values=None):
|
|
"""Validate the list of service definitions."""
|
|
try:
|
|
if not data:
|
|
return _("No service type definition was provided. At least a "
|
|
"service type definition must be provided")
|
|
f_name = _validate_service_defs.__name__
|
|
for svc_def in data:
|
|
try:
|
|
# Do a copy of the original object so we can easily
|
|
# pop out stuff from it
|
|
svc_def_copy = svc_def.copy()
|
|
try:
|
|
svc_name = svc_def_copy.pop(SERVICE_ATTR)
|
|
plugin_name = svc_def_copy.pop(PLUGIN_ATTR)
|
|
except KeyError:
|
|
msg = (_("Required attributes missing in service "
|
|
"definition: %s") % svc_def)
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
# Validate 'service' attribute
|
|
if svc_name not in constants.ALLOWED_SERVICES:
|
|
msg = (_("Service name '%s' unspecified "
|
|
"or invalid") % svc_name)
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
# Validate 'plugin' attribute
|
|
if not plugin_name:
|
|
msg = (_("Plugin name not specified in "
|
|
"service definition %s") % svc_def)
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
# TODO(salvatore-orlando): This code will need to change when
|
|
# multiple plugins for each adv service will be supported
|
|
svc_plugin = manager.QuantumManager.get_service_plugins().get(
|
|
svc_name)
|
|
if not svc_plugin:
|
|
msg = _("No plugin for service '%s'") % svc_name
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
if svc_plugin.get_plugin_name() != plugin_name:
|
|
msg = _("Plugin name '%s' is not correct ") % plugin_name
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
# Validate 'driver' attribute (just check it's a string)
|
|
# FIXME(salvatore-orlando): This should be a list
|
|
# Note: using get() instead of pop() as pop raises if the
|
|
# key is not found, which might happen for the driver
|
|
driver = svc_def_copy.get(DRIVER_ATTR)
|
|
if driver:
|
|
msg = attributes._validate_string(driver,)
|
|
if msg:
|
|
return msg
|
|
del svc_def_copy[DRIVER_ATTR]
|
|
# Anything left - it should be an error
|
|
if svc_def_copy:
|
|
msg = (_("Unparseable attributes found in "
|
|
"service definition %s") % svc_def)
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
except TypeError:
|
|
LOG.exception(_("Exception while parsing service "
|
|
"definition:%s"), svc_def)
|
|
msg = (_("Was expecting a dict for service definition, found "
|
|
"the following: %s") % svc_def)
|
|
LOG.error(_("%(f_name)s: %(msg)s"),
|
|
{'f_name': f_name, 'msg': msg})
|
|
return msg
|
|
except TypeError:
|
|
return (_("%s: provided data are not iterable") %
|
|
_validate_service_defs.__name__)
|
|
|
|
attributes.validators['type:service_definitions'] = _validate_service_defs
|
|
attributes.validators['type:servicetype_ref'] = _validate_servicetype_ref
|
|
|
|
|
|
class Servicetype(extensions.ExtensionDescriptor):
|
|
|
|
@classmethod
|
|
def get_name(cls):
|
|
return _("Quantum Service Type Management")
|
|
|
|
@classmethod
|
|
def get_alias(cls):
|
|
return EXT_ALIAS
|
|
|
|
@classmethod
|
|
def get_description(cls):
|
|
return _("API for retrieving and managing service types for "
|
|
"Quantum advanced services")
|
|
|
|
@classmethod
|
|
def get_namespace(cls):
|
|
return "http://docs.openstack.org/ext/quantum/service-type/api/v1.0"
|
|
|
|
@classmethod
|
|
def get_updated(cls):
|
|
return "2013-01-20T00:00:00-00:00"
|
|
|
|
@classmethod
|
|
def get_resources(cls):
|
|
"""Returns Extended Resource for service type management."""
|
|
my_plurals = [(key, key[:-1]) for key in RESOURCE_ATTRIBUTE_MAP.keys()]
|
|
my_plurals.append(('service_definitions', 'service_definition'))
|
|
attributes.PLURALS.update(dict(my_plurals))
|
|
attr_map = RESOURCE_ATTRIBUTE_MAP[COLLECTION_NAME]
|
|
collection_name = COLLECTION_NAME.replace('_', '-')
|
|
controller = base.create_resource(
|
|
collection_name,
|
|
RESOURCE_NAME,
|
|
servicetype_db.ServiceTypeManager.get_instance(),
|
|
attr_map)
|
|
return [extensions.ResourceExtension(collection_name,
|
|
controller,
|
|
attr_map=attr_map)]
|
|
|
|
def get_extended_resources(self, version):
|
|
if version == "2.0":
|
|
return dict(RESOURCE_ATTRIBUTE_MAP.items())
|
|
else:
|
|
return {}
|