Add ACL support on the API
Right now every authenticated user can perform administration actions on CloudKitty's API. This patch uses oslo.context and oslo.policy to enable a policy engine. Enforcement of the policy will be added in another patchset. Change-Id: Ia88a37b259f12aee00f65876686d12411297b8fb
This commit is contained in:
parent
262b231ce7
commit
1960a4c114
|
@ -72,6 +72,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
|
|||
app_hooks = [
|
||||
hooks.RPCHook(client),
|
||||
hooks.StorageHook(storage_backend),
|
||||
hooks.ContextHook(),
|
||||
]
|
||||
|
||||
app = pecan.make_app(
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
from oslo_context import context
|
||||
from pecan import hooks
|
||||
|
||||
from cloudkitty.common import policy
|
||||
|
||||
|
||||
class RPCHook(hooks.PecanHook):
|
||||
def __init__(self, rcp_client):
|
||||
|
@ -32,3 +35,20 @@ class StorageHook(hooks.PecanHook):
|
|||
|
||||
def before(self, state):
|
||||
state.request.storage_backend = self._storage_backend
|
||||
|
||||
|
||||
class ContextHook(hooks.PecanHook):
|
||||
def before(self, state):
|
||||
headers = state.request.headers
|
||||
|
||||
roles = headers.get('X-Roles', '').split(',')
|
||||
is_admin = policy.check_is_admin(roles)
|
||||
|
||||
creds = {
|
||||
'user': headers.get('X-User') or headers.get('X-User-Id'),
|
||||
'tenant': headers.get('X-Tenant') or headers.get('X-Tenant-Id'),
|
||||
'auth_token': headers.get('X-Auth-Token'),
|
||||
'is_admin': is_admin,
|
||||
}
|
||||
|
||||
state.request.context = context.RequestContext(**creds)
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
# Copyright (c) 2011 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.
|
||||
|
||||
# Borrowed from cinder (cinder/policy.py)
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_policy import policy
|
||||
import six
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
# TODO(gpocentek): provide a proper parent class to handle such exceptions
|
||||
class PolicyNotAuthorized(Exception):
|
||||
message = "Policy doesn't allow %(action)s to be performed."
|
||||
code = 403
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.msg = self.message % kwargs
|
||||
super(PolicyNotAuthorized, self).__init__(self.msg)
|
||||
|
||||
def __unicode__(self):
|
||||
return six.text_type(self.msg)
|
||||
|
||||
|
||||
def init():
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
_ENFORCER = policy.Enforcer(CONF)
|
||||
|
||||
|
||||
def enforce(context, action, target):
|
||||
"""Verifies that the action is valid on the target in this context.
|
||||
|
||||
:param context: cloudkitty 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 object: 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}``
|
||||
|
||||
:raises PolicyNotAuthorized: if verification fails.
|
||||
|
||||
"""
|
||||
init()
|
||||
|
||||
return _ENFORCER.enforce(action, target, context.to_dict(),
|
||||
do_raise=True,
|
||||
exc=PolicyNotAuthorized,
|
||||
action=action)
|
||||
|
||||
|
||||
def check_is_admin(roles):
|
||||
"""Whether or not roles contains 'admin' role according to policy setting.
|
||||
|
||||
"""
|
||||
init()
|
||||
|
||||
# include project_id on target to avoid KeyError if context_is_admin
|
||||
# policy definition is missing, and default admin_or_owner rule
|
||||
# attempts to apply. Since our credentials dict does not include a
|
||||
# project_id, this target can never match as a generic rule.
|
||||
target = {'project_id': ''}
|
||||
credentials = {'roles': roles}
|
||||
|
||||
return _ENFORCER.enforce('context_is_admin', target, credentials)
|
|
@ -71,6 +71,7 @@ mkdir -p %{buildroot}/etc/cloudkitty/
|
|||
#popd
|
||||
|
||||
install -p -D -m 640 etc/cloudkitty/cloudkitty.conf.sample %{buildroot}/%{_sysconfdir}/cloudkitty/cloudkitty.conf
|
||||
install -p -D -m 640 etc/cloudkitty/policy.json %{buildroot}/%{_sysconfdir}/cloudkitty/policy.json
|
||||
|
||||
%description
|
||||
OpenStack Rating-as-a-Service
|
||||
|
@ -114,6 +115,7 @@ Components common to all CloudKitty services
|
|||
%dir %attr(0755,cloudkitty,root) %{_sysconfdir}/cloudkitty
|
||||
%config(noreplace) %{_sysconfdir}/logrotate.d/cloudkitty
|
||||
%config(noreplace) %attr(-, root, cloudkitty) %{_sysconfdir}/cloudkitty/cloudkitty.conf
|
||||
%config(noreplace) %attr(-, root, cloudkitty) %{_sysconfdir}/cloudkitty/policy.json
|
||||
|
||||
%pre common
|
||||
getent group cloudkitty >/dev/null || groupadd -r cloudkitty
|
||||
|
|
|
@ -137,6 +137,7 @@ function configure_cloudkitty {
|
|||
sudo mkdir -m 755 -p $CLOUDKITTY_API_LOG_DIR
|
||||
sudo chown $STACK_USER $CLOUDKITTY_API_LOG_DIR
|
||||
|
||||
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/policy.json $CLOUDKITTY_CONF_DIR
|
||||
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF.sample $CLOUDKITTY_CONF
|
||||
iniset_rpc_backend cloudkitty $CLOUDKITTY_CONF DEFAULT
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"context_is_admin": "role:admin"
|
||||
}
|
|
@ -13,6 +13,8 @@ oslo.messaging
|
|||
oslo.db
|
||||
oslo.i18n
|
||||
oslo.utils
|
||||
oslo.context
|
||||
oslo.policy
|
||||
sqlalchemy
|
||||
six>=1.7.0
|
||||
stevedore
|
||||
|
|
Loading…
Reference in New Issue