Skip to content

Commit

Permalink
Replace admin check with policy check in placement API
Browse files Browse the repository at this point in the history
This sets up a basic framework for policy checking in the placement API
and replaces the current admin check.

There are some limitations here based on oslo.policy limitations. It
turns out that policy rule defaults can only be registered if a policy
file is also used. Since we don't have immediate plans for a placement
policy file the issue is avoided by loading the necessary rule when
policy.Enforcer is initialized. There is also no support for generating
policy files using the oslopolicy CLI scripts. This can all be addressed
later when we determine a path for separating placement policy from Nova
or merging it together.

Although there is an existing nova.policy the changes for placement
policy are implemented in nova.api.openstack.placement.policy to
ease any later extraction that may happen. This is straightforward
because the placement policy code doesn't use any code from
nova.policy.

Co-Authored-By: Chris Dent <cdent@anticdent.org>
Change-Id: Ia62158e0eed50a34114718ee724b038f577c7e87
Partially-Implements: bp placement-api-policy-authz
  • Loading branch information
alaski and cdent committed Oct 31, 2016
1 parent 951a2f4 commit cb9300f
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 6 deletions.
9 changes: 3 additions & 6 deletions nova/api/openstack/placement/handler.py
Expand Up @@ -36,6 +36,7 @@
from nova.api.openstack.placement import util
from nova import exception
from nova.i18n import _, _LE
from nova.api.openstack.placement import policy

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -155,12 +156,8 @@ def __call__(self, environ, start_response):
context = environ['placement.context']
# TODO(cdent): Using is_admin everywhere (except /) is
# insufficiently flexible for future use case but is
# convenient for initial exploration. We will need to
# determine how to manage authorization/policy and
# implement that, probably per handler. Also this is
# just the wrong way to do things, but policy not
# integrated yet.
if 'admin' not in context.to_policy_values()['roles']:
# convenient for initial exploration.
if not policy.placement_authorize(context, 'placement'):
raise webob.exc.HTTPForbidden(
_('admin required'),
json_formatter=util.json_error_formatter)
Expand Down
76 changes: 76 additions & 0 deletions nova/api/openstack/placement/policy.py
@@ -0,0 +1,76 @@
# 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.
"""Policy Enforcement for placement API."""

from oslo_config import cfg
from oslo_log import log as logging
from oslo_policy import policy
from oslo_serialization import jsonutils


CONF = cfg.CONF
LOG = logging.getLogger(__name__)
_ENFORCER_PLACEMENT = None


def placement_init():
"""Init an Enforcer class for placement policy.
This method uses a different list of policies than other parts of Nova.
This is done to facilitate a split out of the placement service later.
"""
global _ENFORCER_PLACEMENT
if not _ENFORCER_PLACEMENT:
# TODO(cdent): Using is_admin everywhere (except /) is
# insufficiently flexible for future use case but is
# convenient for initial exploration. We will need to
# determine how to manage authorization/policy and
# implement that, probably per handler.
rules = policy.Rules.load_json(
jsonutils.dumps({'placement': 'role:admin'}))
# Enforcer is initialized so that the above rule is loaded in and no
# policy file is read.
# TODO(alaski): Register a default rule rather than loading it in like
# this. That requires that a policy file is specified to be read. When
# this is split out such that a placement policy file makes sense then
# change to rule registration.
_ENFORCER_PLACEMENT = policy.Enforcer(CONF, rules=rules,
use_conf=False)


def placement_authorize(context, action, target=None):
"""Verifies that the action is valid on the target in this context.
:param context: RequestContext object
:param action: string representing the action to be checked
: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}``
:return: returns a non-False value (not necessarily "True") if
authorized, and the exact value False if not authorized.
"""
placement_init()
if target is None:
target = {'project_id': context.tenant,
'user_id': context.user}
credentials = context.to_policy_values()
# TODO(alaski): Change this to use authorize() when rules are registered.
# noqa the following line because a hacking check disallows using enforce.
result = _ENFORCER_PLACEMENT.enforce(action, target, credentials,
do_raise=False, exc=None,
action=action)
if result is False:
LOG.debug('Policy check for %(action)s failed with credentials '
'%(credentials)s',
{'action': action, 'credentials': credentials})
return result

0 comments on commit cb9300f

Please sign in to comment.