Merge "Add policy granularity to the encryption API"
This commit is contained in:
commit
4580c56058
@ -25,7 +25,9 @@ from cinder.api import validation
|
||||
from cinder import db
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.policies import base
|
||||
from cinder.policies import volume_type as policy
|
||||
from cinder import policy as cinder_policy
|
||||
from cinder import rpc
|
||||
from cinder.volume import volume_types
|
||||
|
||||
@ -56,10 +58,26 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
||||
else:
|
||||
return False
|
||||
|
||||
def _authorize_policy(self, context, new_policy):
|
||||
# TODO(cl566n): In future release, this _authorize_policy function
|
||||
# can be removed. The call to it can be replaced by
|
||||
# context.authorize(new_policy) once the old
|
||||
# policy.ENCRYPTION_POLICY is deprecated.
|
||||
|
||||
using_old_action = cinder_policy.verify_deprecated_policy(
|
||||
policy.ENCRYPTION_POLICY,
|
||||
new_policy,
|
||||
base.RULE_ADMIN_API,
|
||||
context)
|
||||
|
||||
if not using_old_action:
|
||||
context.authorize(new_policy)
|
||||
|
||||
def index(self, req, type_id):
|
||||
"""Returns the encryption specs for a given volume type."""
|
||||
context = req.environ['cinder.context']
|
||||
context.authorize(policy.ENCRYPTION_POLICY)
|
||||
self._authorize_policy(context, policy.GET_ENCRYPTION_POLICY)
|
||||
|
||||
self._check_type(context, type_id)
|
||||
return self._get_volume_type_encryption(context, type_id)
|
||||
|
||||
@ -67,7 +85,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
||||
def create(self, req, type_id, body):
|
||||
"""Create encryption specs for an existing volume type."""
|
||||
context = req.environ['cinder.context']
|
||||
context.authorize(policy.ENCRYPTION_POLICY)
|
||||
self._authorize_policy(context, policy.CREATE_ENCRYPTION_POLICY)
|
||||
|
||||
key_size = body['encryption'].get('key_size')
|
||||
if key_size is not None:
|
||||
@ -95,7 +113,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
||||
def update(self, req, type_id, id, body):
|
||||
"""Update encryption specs for a given volume type."""
|
||||
context = req.environ['cinder.context']
|
||||
context.authorize(policy.ENCRYPTION_POLICY)
|
||||
self._authorize_policy(context, policy.UPDATE_ENCRYPTION_POLICY)
|
||||
|
||||
key_size = body['encryption'].get('key_size')
|
||||
if key_size is not None:
|
||||
@ -119,7 +137,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
||||
def show(self, req, type_id, id):
|
||||
"""Return a single encryption item."""
|
||||
context = req.environ['cinder.context']
|
||||
context.authorize(policy.ENCRYPTION_POLICY)
|
||||
self._authorize_policy(context, policy.GET_ENCRYPTION_POLICY)
|
||||
|
||||
self._check_type(context, type_id)
|
||||
|
||||
@ -133,7 +151,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
||||
def delete(self, req, type_id, id):
|
||||
"""Delete encryption specs for a given volume type."""
|
||||
context = req.environ['cinder.context']
|
||||
context.authorize(policy.ENCRYPTION_POLICY)
|
||||
self._authorize_policy(context, policy.DELETE_ENCRYPTION_POLICY)
|
||||
|
||||
if self._encrypted_type_in_use(context, type_id):
|
||||
expl = _('Cannot delete encryption specs. Volume type in use.')
|
||||
|
@ -20,6 +20,11 @@ from cinder.policies import base
|
||||
|
||||
MANAGE_POLICY = "volume_extension:types_manage"
|
||||
ENCRYPTION_POLICY = "volume_extension:volume_type_encryption"
|
||||
BASE_POLICY_RULE = 'rule:%s' % ENCRYPTION_POLICY
|
||||
CREATE_ENCRYPTION_POLICY = "volume_extension:volume_type_encryption:create"
|
||||
GET_ENCRYPTION_POLICY = "volume_extension:volume_type_encryption:get"
|
||||
UPDATE_ENCRYPTION_POLICY = "volume_extension:volume_type_encryption:update"
|
||||
DELETE_ENCRYPTION_POLICY = "volume_extension:volume_type_encryption:delete"
|
||||
QOS_POLICY = "volume_extension:access_types_qos_specs_id"
|
||||
EXTRA_SPEC_POLICY = "volume_extension:access_types_extra_specs"
|
||||
GET_POLICY = "volume_extension:type_get"
|
||||
@ -68,7 +73,8 @@ volume_type_policies = [
|
||||
name=ENCRYPTION_POLICY,
|
||||
check_str=base.RULE_ADMIN_API,
|
||||
description="List, show, create, update and delete volume "
|
||||
"type encryption.",
|
||||
"type encryption. This is deprecated in the Stein "
|
||||
"release and will be removed in the future.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
@ -91,6 +97,50 @@ volume_type_policies = [
|
||||
'path': '/types/{type_id}/encryption/{encryption_id}'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=CREATE_ENCRYPTION_POLICY,
|
||||
check_str=BASE_POLICY_RULE,
|
||||
description="Create volume type encryption.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/types/{type_id}/encryption'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=GET_ENCRYPTION_POLICY,
|
||||
check_str=BASE_POLICY_RULE,
|
||||
description="Show, list volume type encryption.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/types/{type_id}/encryption/{encryption_id}'
|
||||
},
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/types/{type_id}/encryption'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=UPDATE_ENCRYPTION_POLICY,
|
||||
check_str=BASE_POLICY_RULE,
|
||||
description="Update volume type encryption.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/types/{type_id}/encryption/{encryption_id}'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=DELETE_ENCRYPTION_POLICY,
|
||||
check_str=BASE_POLICY_RULE,
|
||||
description="Delete volume type encryption.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': '/types/{type_id}/encryption/{encryption_id}'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=EXTRA_SPEC_POLICY,
|
||||
check_str=base.RULE_ADMIN_API,
|
||||
|
@ -178,3 +178,33 @@ def check_is_admin(context):
|
||||
credentials = context.to_policy_values()
|
||||
target = credentials
|
||||
return _ENFORCER.authorize('context_is_admin', target, credentials)
|
||||
|
||||
|
||||
def verify_deprecated_policy(old_policy, new_policy, default_rule, context):
|
||||
"""Check the rule of the deprecated policy action
|
||||
|
||||
If the current rule of the deprecated policy action is set to a non-default
|
||||
value, then a warning message is logged stating that the new policy
|
||||
action should be used to dictate permissions as the old policy action is
|
||||
being deprecated.
|
||||
|
||||
:param old_policy: policy action that is being deprecated
|
||||
:param new_policy: policy action that is replacing old_policy
|
||||
:param default_rule: the old_policy action default rule value
|
||||
:param context: the cinder context
|
||||
"""
|
||||
|
||||
if _ENFORCER:
|
||||
current_rule = str(_ENFORCER.rules[old_policy])
|
||||
else:
|
||||
current_rule = None
|
||||
|
||||
if current_rule != default_rule:
|
||||
LOG.warning('Start using the new action %(new_policy)s. The existing '
|
||||
'action %(old_policy)s is being deprecated and will be '
|
||||
'removed in future release.',
|
||||
{'new_policy': new_policy, 'old_policy': old_policy})
|
||||
|
||||
context.authorize(old_policy)
|
||||
return True
|
||||
return False
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
import os.path
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslo_policy import policy as oslo_policy
|
||||
@ -75,6 +76,9 @@ class PolicyTestCase(test.TestCase):
|
||||
"role:admin"),
|
||||
oslo_policy.RuleDefault("test:uppercase_admin",
|
||||
"role:ADMIN"),
|
||||
oslo_policy.RuleDefault("old_action_not_default", "@"),
|
||||
oslo_policy.RuleDefault("new_action", "@"),
|
||||
oslo_policy.RuleDefault("old_action_default", "rule:admin_api"),
|
||||
]
|
||||
policy.reset()
|
||||
policy.init()
|
||||
@ -129,3 +133,26 @@ class PolicyTestCase(test.TestCase):
|
||||
roles=['AdMiN'])
|
||||
policy.authorize(admin_context, lowercase_action, self.target)
|
||||
policy.authorize(admin_context, uppercase_action, self.target)
|
||||
|
||||
@mock.patch.object(policy.LOG, 'warning')
|
||||
def test_verify_deprecated_policy_using_old_action(self, mock_warning):
|
||||
|
||||
old_policy = "old_action_not_default"
|
||||
new_policy = "new_action"
|
||||
default_rule = "rule:admin_api"
|
||||
|
||||
using_old_action = policy.verify_deprecated_policy(
|
||||
old_policy, new_policy, default_rule, self.context)
|
||||
|
||||
self.assertTrue(mock_warning.called)
|
||||
self.assertTrue(using_old_action)
|
||||
|
||||
def test_verify_deprecated_policy_using_new_action(self):
|
||||
old_policy = "old_action_default"
|
||||
new_policy = "new_action"
|
||||
default_rule = "rule:admin_api"
|
||||
|
||||
using_old_action = policy.verify_deprecated_policy(
|
||||
old_policy, new_policy, default_rule, self.context)
|
||||
|
||||
self.assertFalse(using_old_action)
|
||||
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Add granularity to the ``volume_extension:volume_type_encryption``
|
||||
policy with the addition of distinct actions for create, get, update,
|
||||
and delete:
|
||||
|
||||
- ``volume_extension:volume_type_encryption:create``
|
||||
- ``volume_extension:volume_type_encryption:get``
|
||||
- ``volume_extension:volume_type_encryption:update``
|
||||
- ``volume_extension:volume_type_encryption:delete``
|
||||
|
||||
To address backwards compatibility, the new rules added to the
|
||||
volume_type.py policy file, default to the existing rule,
|
||||
``volume_extension:volume_type_encryption``, if it is set to a
|
||||
non-default value.
|
Loading…
x
Reference in New Issue
Block a user