Policy-in-code servers rules
This adds the basic framework for registering and using default policy rules. Rules should be defined and returned from a module in nova/policies/, and then added to the list in nova/policies/__init__.py. A new context.can() method has been added for policy enforcement of registered rules. It has the same parameters as the enforce() method currently being used. To establish the full pattern for usage the policy checks in the servers API module have been registered and converted to the new usage. Now that some policy checks are registered they're being used properly by tests. Some tests have been updated so that the instance project_id matches the context project_id in order to pass the 'admin_or_owner' check. Change-Id: I71b3d1233255125cb280a000b990329f5b03fdfd Partially-Implements: bp policy-in-code
This commit is contained in:
@@ -8,28 +8,7 @@
|
|||||||
"admin_api": "is_admin:True",
|
"admin_api": "is_admin:True",
|
||||||
|
|
||||||
"network:attach_external_network": "is_admin:True",
|
"network:attach_external_network": "is_admin:True",
|
||||||
"os_compute_api:servers:detail:get_all_tenants": "is_admin:True",
|
|
||||||
"os_compute_api:servers:index:get_all_tenants": "is_admin:True",
|
|
||||||
"os_compute_api:servers:confirm_resize": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:create": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:create:attach_network": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:create:attach_volume": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:create:forced_host": "rule:admin_api",
|
|
||||||
"os_compute_api:servers:delete": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:update": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:detail": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:index": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:reboot": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:rebuild": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:resize": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:revert_resize": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:show": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:show:host_status": "rule:admin_api",
|
"os_compute_api:servers:show:host_status": "rule:admin_api",
|
||||||
"os_compute_api:servers:create_image": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:create_image:allow_volume_backed": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:start": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:stop": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:trigger_crash_dump": "rule:admin_or_owner",
|
|
||||||
"os_compute_api:servers:migrations:force_complete": "rule:admin_api",
|
"os_compute_api:servers:migrations:force_complete": "rule:admin_api",
|
||||||
"os_compute_api:servers:migrations:delete": "rule:admin_api",
|
"os_compute_api:servers:migrations:delete": "rule:admin_api",
|
||||||
"os_compute_api:servers:discoverable": "@",
|
"os_compute_api:servers:discoverable": "@",
|
||||||
|
@@ -43,6 +43,7 @@ from nova.i18n import _
|
|||||||
from nova.i18n import _LW
|
from nova.i18n import _LW
|
||||||
from nova.image import glance
|
from nova.image import glance
|
||||||
from nova import objects
|
from nova import objects
|
||||||
|
from nova.policies import servers as server_policies
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
ALIAS = 'servers'
|
ALIAS = 'servers'
|
||||||
@@ -51,7 +52,6 @@ TAG_SEARCH_FILTERS = ('tags', 'tags-any', 'not-tags', 'not-tags-any')
|
|||||||
CONF = nova.conf.CONF
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
authorize = extensions.os_compute_authorizer(ALIAS)
|
|
||||||
|
|
||||||
|
|
||||||
class ServersController(wsgi.Controller):
|
class ServersController(wsgi.Controller):
|
||||||
@@ -273,7 +273,7 @@ class ServersController(wsgi.Controller):
|
|||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a list of server names and ids for a given user."""
|
"""Returns a list of server names and ids for a given user."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action="index")
|
context.can(server_policies.get_name('index'))
|
||||||
try:
|
try:
|
||||||
servers = self._get_servers(req, is_detail=False)
|
servers = self._get_servers(req, is_detail=False)
|
||||||
except exception.Invalid as err:
|
except exception.Invalid as err:
|
||||||
@@ -284,7 +284,7 @@ class ServersController(wsgi.Controller):
|
|||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Returns a list of server details for a given user."""
|
"""Returns a list of server details for a given user."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action="detail")
|
context.can(server_policies.get_name('detail'))
|
||||||
try:
|
try:
|
||||||
servers = self._get_servers(req, is_detail=True)
|
servers = self._get_servers(req, is_detail=True)
|
||||||
except exception.Invalid as err:
|
except exception.Invalid as err:
|
||||||
@@ -383,9 +383,9 @@ class ServersController(wsgi.Controller):
|
|||||||
elevated = None
|
elevated = None
|
||||||
if all_tenants:
|
if all_tenants:
|
||||||
if is_detail:
|
if is_detail:
|
||||||
authorize(context, action="detail:get_all_tenants")
|
context.can(server_policies.get_name('detail:get_all_tenants'))
|
||||||
else:
|
else:
|
||||||
authorize(context, action="index:get_all_tenants")
|
context.can(server_policies.get_name('index:get_all_tenants'))
|
||||||
elevated = context.elevated()
|
elevated = context.elevated()
|
||||||
else:
|
else:
|
||||||
if context.project_id:
|
if context.project_id:
|
||||||
@@ -538,7 +538,7 @@ class ServersController(wsgi.Controller):
|
|||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Returns server details by server id."""
|
"""Returns server details by server id."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action="show")
|
context.can(server_policies.get_name('show'))
|
||||||
instance = self._get_server(context, req, id, is_detail=True)
|
instance = self._get_server(context, req, id, is_detail=True)
|
||||||
return self._view_builder.show(req, instance)
|
return self._view_builder.show(req, instance)
|
||||||
|
|
||||||
@@ -585,7 +585,7 @@ class ServersController(wsgi.Controller):
|
|||||||
'project_id': context.project_id,
|
'project_id': context.project_id,
|
||||||
'user_id': context.user_id,
|
'user_id': context.user_id,
|
||||||
'availability_zone': availability_zone}
|
'availability_zone': availability_zone}
|
||||||
authorize(context, target, 'create')
|
context.can(server_policies.get_name('create'), target)
|
||||||
|
|
||||||
# TODO(Shao He, Feng) move this policy check to os-availabilty-zone
|
# TODO(Shao He, Feng) move this policy check to os-availabilty-zone
|
||||||
# extension after refactor it.
|
# extension after refactor it.
|
||||||
@@ -596,13 +596,14 @@ class ServersController(wsgi.Controller):
|
|||||||
except exception.InvalidInput as err:
|
except exception.InvalidInput as err:
|
||||||
raise exc.HTTPBadRequest(explanation=six.text_type(err))
|
raise exc.HTTPBadRequest(explanation=six.text_type(err))
|
||||||
if host or node:
|
if host or node:
|
||||||
authorize(context, {}, 'create:forced_host')
|
context.can(server_policies.get_name('create:forced_host'), {})
|
||||||
|
|
||||||
block_device_mapping = create_kwargs.get("block_device_mapping")
|
block_device_mapping = create_kwargs.get("block_device_mapping")
|
||||||
# TODO(Shao He, Feng) move this policy check to os-block-device-mapping
|
# TODO(Shao He, Feng) move this policy check to os-block-device-mapping
|
||||||
# extension after refactor it.
|
# extension after refactor it.
|
||||||
if block_device_mapping:
|
if block_device_mapping:
|
||||||
authorize(context, target, 'create:attach_volume')
|
context.can(server_policies.get_name('create:attach_volume'),
|
||||||
|
target)
|
||||||
|
|
||||||
image_uuid = self._image_from_req_data(server_dict, create_kwargs)
|
image_uuid = self._image_from_req_data(server_dict, create_kwargs)
|
||||||
|
|
||||||
@@ -626,7 +627,8 @@ class ServersController(wsgi.Controller):
|
|||||||
requested_networks)
|
requested_networks)
|
||||||
|
|
||||||
if requested_networks and len(requested_networks):
|
if requested_networks and len(requested_networks):
|
||||||
authorize(context, target, 'create:attach_network')
|
context.can(server_policies.get_name('create:attach_network'),
|
||||||
|
target)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
flavor_id = self._flavor_id_from_req_data(body)
|
flavor_id = self._flavor_id_from_req_data(body)
|
||||||
@@ -801,7 +803,7 @@ class ServersController(wsgi.Controller):
|
|||||||
resize_schema['properties']['resize']['properties'].update(schema)
|
resize_schema['properties']['resize']['properties'].update(schema)
|
||||||
|
|
||||||
def _delete(self, context, req, instance_uuid):
|
def _delete(self, context, req, instance_uuid):
|
||||||
authorize(context, action='delete')
|
context.can(server_policies.get_name('delete'))
|
||||||
instance = self._get_server(context, req, instance_uuid)
|
instance = self._get_server(context, req, instance_uuid)
|
||||||
if CONF.reclaim_instance_interval:
|
if CONF.reclaim_instance_interval:
|
||||||
try:
|
try:
|
||||||
@@ -823,7 +825,7 @@ class ServersController(wsgi.Controller):
|
|||||||
|
|
||||||
ctxt = req.environ['nova.context']
|
ctxt = req.environ['nova.context']
|
||||||
update_dict = {}
|
update_dict = {}
|
||||||
authorize(ctxt, action='update')
|
ctxt.can(server_policies.get_name('update'))
|
||||||
|
|
||||||
if 'name' in body['server']:
|
if 'name' in body['server']:
|
||||||
update_dict['display_name'] = common.normalize_name(
|
update_dict['display_name'] = common.normalize_name(
|
||||||
@@ -857,7 +859,7 @@ class ServersController(wsgi.Controller):
|
|||||||
@wsgi.action('confirmResize')
|
@wsgi.action('confirmResize')
|
||||||
def _action_confirm_resize(self, req, id, body):
|
def _action_confirm_resize(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='confirm_resize')
|
context.can(server_policies.get_name('confirm_resize'))
|
||||||
instance = self._get_server(context, req, id)
|
instance = self._get_server(context, req, id)
|
||||||
try:
|
try:
|
||||||
self.compute_api.confirm_resize(context, instance)
|
self.compute_api.confirm_resize(context, instance)
|
||||||
@@ -877,7 +879,7 @@ class ServersController(wsgi.Controller):
|
|||||||
@wsgi.action('revertResize')
|
@wsgi.action('revertResize')
|
||||||
def _action_revert_resize(self, req, id, body):
|
def _action_revert_resize(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='revert_resize')
|
context.can(server_policies.get_name('revert_resize'))
|
||||||
instance = self._get_server(context, req, id)
|
instance = self._get_server(context, req, id)
|
||||||
try:
|
try:
|
||||||
self.compute_api.revert_resize(context, instance)
|
self.compute_api.revert_resize(context, instance)
|
||||||
@@ -903,7 +905,7 @@ class ServersController(wsgi.Controller):
|
|||||||
|
|
||||||
reboot_type = body['reboot']['type'].upper()
|
reboot_type = body['reboot']['type'].upper()
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='reboot')
|
context.can(server_policies.get_name('reboot'))
|
||||||
instance = self._get_server(context, req, id)
|
instance = self._get_server(context, req, id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -917,7 +919,7 @@ class ServersController(wsgi.Controller):
|
|||||||
def _resize(self, req, instance_id, flavor_id, **kwargs):
|
def _resize(self, req, instance_id, flavor_id, **kwargs):
|
||||||
"""Begin the resize process with given instance/flavor."""
|
"""Begin the resize process with given instance/flavor."""
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
authorize(context, action='resize')
|
context.can(server_policies.get_name('resize'))
|
||||||
instance = self._get_server(context, req, instance_id)
|
instance = self._get_server(context, req, instance_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1023,7 +1025,7 @@ class ServersController(wsgi.Controller):
|
|||||||
password = self._get_server_admin_password(rebuild_dict)
|
password = self._get_server_admin_password(rebuild_dict)
|
||||||
|
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='rebuild')
|
context.can(server_policies.get_name('rebuild'))
|
||||||
instance = self._get_server(context, req, id)
|
instance = self._get_server(context, req, id)
|
||||||
|
|
||||||
attr_map = {
|
attr_map = {
|
||||||
@@ -1098,7 +1100,7 @@ class ServersController(wsgi.Controller):
|
|||||||
def _action_create_image(self, req, id, body):
|
def _action_create_image(self, req, id, body):
|
||||||
"""Snapshot a server instance."""
|
"""Snapshot a server instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='create_image')
|
context.can(server_policies.get_name('create_image'))
|
||||||
|
|
||||||
entity = body["createImage"]
|
entity = body["createImage"]
|
||||||
image_name = common.normalize_name(entity["name"])
|
image_name = common.normalize_name(entity["name"])
|
||||||
@@ -1114,7 +1116,8 @@ class ServersController(wsgi.Controller):
|
|||||||
try:
|
try:
|
||||||
if compute_utils.is_volume_backed_instance(context, instance,
|
if compute_utils.is_volume_backed_instance(context, instance,
|
||||||
bdms):
|
bdms):
|
||||||
authorize(context, action="create_image:allow_volume_backed")
|
context.can(server_policies.get_name(
|
||||||
|
'create_image:allow_volume_backed'))
|
||||||
image = self.compute_api.snapshot_volume_backed(
|
image = self.compute_api.snapshot_volume_backed(
|
||||||
context,
|
context,
|
||||||
instance,
|
instance,
|
||||||
@@ -1175,7 +1178,7 @@ class ServersController(wsgi.Controller):
|
|||||||
"""Start an instance."""
|
"""Start an instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
instance = self._get_instance(context, id)
|
instance = self._get_instance(context, id)
|
||||||
authorize(context, instance, 'start')
|
context.can(server_policies.get_name('start'), instance)
|
||||||
LOG.debug('start instance', instance=instance)
|
LOG.debug('start instance', instance=instance)
|
||||||
try:
|
try:
|
||||||
self.compute_api.start(context, instance)
|
self.compute_api.start(context, instance)
|
||||||
@@ -1194,7 +1197,7 @@ class ServersController(wsgi.Controller):
|
|||||||
"""Stop an instance."""
|
"""Stop an instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
instance = self._get_instance(context, id)
|
instance = self._get_instance(context, id)
|
||||||
authorize(context, instance, 'stop')
|
context.can(server_policies.get_name('stop'), instance)
|
||||||
LOG.debug('stop instance', instance=instance)
|
LOG.debug('stop instance', instance=instance)
|
||||||
try:
|
try:
|
||||||
self.compute_api.stop(context, instance)
|
self.compute_api.stop(context, instance)
|
||||||
@@ -1215,7 +1218,7 @@ class ServersController(wsgi.Controller):
|
|||||||
"""Trigger crash dump in an instance"""
|
"""Trigger crash dump in an instance"""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
instance = self._get_instance(context, id)
|
instance = self._get_instance(context, id)
|
||||||
authorize(context, instance, 'trigger_crash_dump')
|
context.can(server_policies.get_name('trigger_crash_dump'), instance)
|
||||||
try:
|
try:
|
||||||
self.compute_api.trigger_crash_dump(context, instance)
|
self.compute_api.trigger_crash_dump(context, instance)
|
||||||
except exception.InstanceInvalidState as state_error:
|
except exception.InstanceInvalidState as state_error:
|
||||||
|
@@ -218,6 +218,12 @@ class RequestContext(context.RequestContext):
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def can(self, rule, target=None):
|
||||||
|
if target is None:
|
||||||
|
target = {'project_id': self.project_id,
|
||||||
|
'user_id': self.user_id}
|
||||||
|
return policy.authorize(self, rule, target)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<Context %s>" % self.to_dict()
|
return "<Context %s>" % self.to_dict()
|
||||||
|
|
||||||
|
24
nova/policies/__init__.py
Normal file
24
nova/policies/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
from nova.policies import base
|
||||||
|
from nova.policies import servers
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return itertools.chain(
|
||||||
|
base.list_rules(),
|
||||||
|
servers.list_rules()
|
||||||
|
)
|
24
nova/policies/base.py
Normal file
24
nova/policies/base.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
policy.RuleDefault('context_is_admin', 'role:admin'),
|
||||||
|
policy.RuleDefault('admin_or_owner',
|
||||||
|
'is_admin:True or project_id:%(project_id)s'),
|
||||||
|
policy.RuleDefault('admin_api', 'is_admin:True'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return rules
|
53
nova/policies/servers.py
Normal file
53
nova/policies/servers.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
|
||||||
|
RULE_AOO = 'rule:admin_or_owner'
|
||||||
|
|
||||||
|
|
||||||
|
def get_name(action=None):
|
||||||
|
name = 'os_compute_api:servers'
|
||||||
|
if action:
|
||||||
|
name = name + ':%s' % action
|
||||||
|
return name
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
policy.RuleDefault(get_name('index'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('detail'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('detail:get_all_tenants'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('index:get_all_tenants'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('show'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create:forced_host'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create:attach_volume'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create:attach_network'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('delete'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('update'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('confirm_resize'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('revert_resize'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('reboot'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('resize'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('rebuild'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create_image'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('create_image:allow_volume_backed'),
|
||||||
|
RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('start'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('stop'), RULE_AOO),
|
||||||
|
policy.RuleDefault(get_name('trigger_crash_dump'), RULE_AOO),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return rules
|
@@ -22,6 +22,8 @@ from oslo_policy import policy
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova.i18n import _LE
|
||||||
|
from nova import policies
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -55,6 +57,7 @@ def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
|
|||||||
rules=rules,
|
rules=rules,
|
||||||
default_rule=default_rule,
|
default_rule=default_rule,
|
||||||
use_conf=use_conf)
|
use_conf=use_conf)
|
||||||
|
register_rules(_ENFORCER)
|
||||||
|
|
||||||
|
|
||||||
def set_rules(rules, overwrite=True, use_conf=False):
|
def set_rules(rules, overwrite=True, use_conf=False):
|
||||||
@@ -70,6 +73,8 @@ def set_rules(rules, overwrite=True, use_conf=False):
|
|||||||
_ENFORCER.set_rules(rules, overwrite, use_conf)
|
_ENFORCER.set_rules(rules, overwrite, use_conf)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(alaski): All users of this method should move over to authorize() as
|
||||||
|
# policies are registered and ultimately this should be removed.
|
||||||
def enforce(context, action, target, do_raise=True, exc=None):
|
def enforce(context, action, target, do_raise=True, exc=None):
|
||||||
"""Verifies that the action is valid on the target in this context.
|
"""Verifies that the action is valid on the target in this context.
|
||||||
|
|
||||||
@@ -108,6 +113,53 @@ def enforce(context, action, target, do_raise=True, exc=None):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def authorize(context, action, target, do_raise=True, exc=None):
|
||||||
|
"""Verifies that the action is valid on the target in this context.
|
||||||
|
|
||||||
|
:param context: nova context
|
||||||
|
:param action: string representing the action to be checked
|
||||||
|
this should be colon separated for clarity.
|
||||||
|
i.e. ``compute:create_instance``,
|
||||||
|
``compute:attach_volume``,
|
||||||
|
``volume:attach_volume``
|
||||||
|
:param target: dictionary representing the object of the action
|
||||||
|
for object creation this should be a dictionary representing the
|
||||||
|
location of the object e.g. ``{'project_id': context.project_id}``
|
||||||
|
:param do_raise: if True (the default), raises PolicyNotAuthorized;
|
||||||
|
if False, returns False
|
||||||
|
:param exc: Class of the exception to raise if the check fails.
|
||||||
|
Any remaining arguments passed to :meth:`enforce` (both
|
||||||
|
positional and keyword arguments) will be passed to
|
||||||
|
the exception class. If not specified,
|
||||||
|
:class:`PolicyNotAuthorized` will be used.
|
||||||
|
|
||||||
|
:raises nova.exception.PolicyNotAuthorized: if verification fails
|
||||||
|
and do_raise is True. Or if 'exc' is specified it will raise an
|
||||||
|
exception of that type.
|
||||||
|
|
||||||
|
:return: returns a non-False value (not necessarily "True") if
|
||||||
|
authorized, and the exact value False if not authorized and
|
||||||
|
do_raise is False.
|
||||||
|
"""
|
||||||
|
init()
|
||||||
|
credentials = context.to_dict()
|
||||||
|
if not exc:
|
||||||
|
exc = exception.PolicyNotAuthorized
|
||||||
|
try:
|
||||||
|
result = _ENFORCER.authorize(action, target, credentials,
|
||||||
|
do_raise=do_raise, exc=exc, action=action)
|
||||||
|
except policy.PolicyNotRegistered:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception(_LE('Policy not registered'))
|
||||||
|
except Exception:
|
||||||
|
credentials.pop('auth_token', None)
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.debug('Policy check for %(action)s failed with credentials '
|
||||||
|
'%(credentials)s',
|
||||||
|
{'action': action, 'credentials': credentials})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def check_is_admin(context):
|
def check_is_admin(context):
|
||||||
"""Whether or not roles contains 'admin' role according to policy setting.
|
"""Whether or not roles contains 'admin' role according to policy setting.
|
||||||
|
|
||||||
@@ -140,3 +192,7 @@ class IsAdminCheck(policy.Check):
|
|||||||
def get_rules():
|
def get_rules():
|
||||||
if _ENFORCER:
|
if _ENFORCER:
|
||||||
return _ENFORCER.rules
|
return _ENFORCER.rules
|
||||||
|
|
||||||
|
|
||||||
|
def register_rules(enforcer):
|
||||||
|
enforcer.register_defaults(policies.list_rules())
|
||||||
|
@@ -1614,7 +1614,8 @@ class ServersControllerRebuildInstanceTest(ControllerTest):
|
|||||||
if uuid == 'test_inst':
|
if uuid == 'test_inst':
|
||||||
raise webob.exc.HTTPNotFound(explanation='fakeout')
|
raise webob.exc.HTTPNotFound(explanation='fakeout')
|
||||||
return fakes.stub_instance_obj(None,
|
return fakes.stub_instance_obj(None,
|
||||||
vm_state=vm_states.ACTIVE)
|
vm_state=vm_states.ACTIVE,
|
||||||
|
project_id='fake')
|
||||||
|
|
||||||
self.useFixture(
|
self.useFixture(
|
||||||
fixtures.MonkeyPatch('nova.api.openstack.compute.servers.'
|
fixtures.MonkeyPatch('nova.api.openstack.compute.servers.'
|
||||||
@@ -2101,7 +2102,8 @@ class ServersControllerTriggerCrashDumpTest(ControllerTest):
|
|||||||
super(ServersControllerTriggerCrashDumpTest, self).setUp()
|
super(ServersControllerTriggerCrashDumpTest, self).setUp()
|
||||||
|
|
||||||
self.instance = fakes.stub_instance_obj(None,
|
self.instance = fakes.stub_instance_obj(None,
|
||||||
vm_state=vm_states.ACTIVE)
|
vm_state=vm_states.ACTIVE,
|
||||||
|
project_id='fake')
|
||||||
|
|
||||||
def fake_get(ctrl, ctxt, uuid):
|
def fake_get(ctrl, ctxt, uuid):
|
||||||
if uuid != FAKE_UUID:
|
if uuid != FAKE_UUID:
|
||||||
|
@@ -323,6 +323,8 @@ def get_fake_uuid(token=0):
|
|||||||
|
|
||||||
def fake_instance_get(**kwargs):
|
def fake_instance_get(**kwargs):
|
||||||
def _return_server(context, uuid, columns_to_join=None, use_slave=False):
|
def _return_server(context, uuid, columns_to_join=None, use_slave=False):
|
||||||
|
if 'project_id' not in kwargs:
|
||||||
|
kwargs['project_id'] = 'fake'
|
||||||
return stub_instance(1, **kwargs)
|
return stub_instance(1, **kwargs)
|
||||||
return _return_server
|
return _return_server
|
||||||
|
|
||||||
|
@@ -21,28 +21,7 @@ policy_data = """
|
|||||||
|
|
||||||
"context_is_admin": "role:admin or role:administrator",
|
"context_is_admin": "role:admin or role:administrator",
|
||||||
|
|
||||||
"os_compute_api:servers:confirm_resize": "",
|
|
||||||
"os_compute_api:servers:create": "",
|
|
||||||
"os_compute_api:servers:create:attach_network": "",
|
|
||||||
"os_compute_api:servers:create:attach_volume": "",
|
|
||||||
"os_compute_api:servers:create:forced_host": "",
|
|
||||||
"os_compute_api:servers:delete": "",
|
|
||||||
"os_compute_api:servers:detail": "",
|
|
||||||
"os_compute_api:servers:detail:get_all_tenants": "",
|
|
||||||
"os_compute_api:servers:index": "",
|
|
||||||
"os_compute_api:servers:index:get_all_tenants": "",
|
|
||||||
"os_compute_api:servers:reboot": "",
|
|
||||||
"os_compute_api:servers:rebuild": "",
|
|
||||||
"os_compute_api:servers:resize": "",
|
|
||||||
"os_compute_api:servers:revert_resize": "",
|
|
||||||
"os_compute_api:servers:show": "",
|
|
||||||
"os_compute_api:servers:show:host_status": "",
|
"os_compute_api:servers:show:host_status": "",
|
||||||
"os_compute_api:servers:create_image": "",
|
|
||||||
"os_compute_api:servers:create_image:allow_volume_backed": "",
|
|
||||||
"os_compute_api:servers:update": "",
|
|
||||||
"os_compute_api:servers:start": "",
|
|
||||||
"os_compute_api:servers:stop": "",
|
|
||||||
"os_compute_api:servers:trigger_crash_dump": "",
|
|
||||||
"os_compute_api:servers:migrations:delete": "rule:admin_api",
|
"os_compute_api:servers:migrations:delete": "rule:admin_api",
|
||||||
"os_compute_api:servers:migrations:force_complete": "",
|
"os_compute_api:servers:migrations:force_complete": "",
|
||||||
"os_compute_api:servers:migrations:index": "rule:admin_api",
|
"os_compute_api:servers:migrations:index": "rule:admin_api",
|
||||||
|
Reference in New Issue
Block a user