limit access to certificate and container:create
Only the user who creates the bay can get the certificate and call the certificate signing request of the bay and create containers in the bay, which is needed by [1]. [1] https://github.com/openstack/magnum/blob/master/specs/ create-trustee-user-for-each-bay.rst Change-Id: Id959b76cb136ffbb0e6bcb8c3b83e02b30de66cf Closes-Bug: #1536883 Partially-Implements: blueprint create-trustee-user-for-each-bay
This commit is contained in:
parent
138483a0bd
commit
ce5b55dd31
|
@ -3,6 +3,7 @@
|
|||
"admin_or_owner": "is_admin:True or project_id:%(project_id)s",
|
||||
"default": "rule:admin_or_owner",
|
||||
"admin_api": "rule:context_is_admin",
|
||||
"admin_or_user": "is_admin:True or user_id:%(user_id)s",
|
||||
|
||||
"bay:create": "rule:default",
|
||||
"bay:delete": "rule:default",
|
||||
|
@ -40,15 +41,15 @@
|
|||
"service:get_all": "rule:default",
|
||||
"service:update": "rule:default",
|
||||
|
||||
"container:create": "rule:default",
|
||||
"container:delete": "rule:default",
|
||||
"container:create": "rule:admin_or_user",
|
||||
"container:delete": "rule:admin_or_user",
|
||||
"container:detail": "rule:default",
|
||||
"container:get": "rule:default",
|
||||
"container:get_all": "rule:default",
|
||||
"container:update": "rule:default",
|
||||
"container:update": "rule:admin_or_user",
|
||||
|
||||
"certificate:create": "rule:default",
|
||||
"certificate:get": "rule:default",
|
||||
"certificate:create": "rule:admin_or_user",
|
||||
"certificate:get": "rule:admin_or_user",
|
||||
|
||||
"magnum-service:get_all": "rule:admin_api"
|
||||
}
|
||||
|
|
|
@ -125,30 +125,34 @@ class CertificateController(rest.RestController):
|
|||
}
|
||||
|
||||
@wsme_pecan.wsexpose(Certificate, types.uuid_or_name)
|
||||
@policy.enforce_wsgi("certificate", "get")
|
||||
def get_one(self, bay_ident):
|
||||
"""Retrieve information about the given certificate.
|
||||
|
||||
:param bay_ident: UUID of a bay or
|
||||
logical name of the bay.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
bay = api_utils.get_resource('Bay', bay_ident)
|
||||
policy.enforce(context, 'certificate:get', bay,
|
||||
action='certificate:get')
|
||||
certificate = pecan.request.rpcapi.get_ca_certificate(bay)
|
||||
return Certificate.convert_with_links(certificate)
|
||||
|
||||
@wsme_pecan.wsexpose(Certificate, body=Certificate, status_code=201)
|
||||
@policy.enforce_wsgi("certificate", "create")
|
||||
def post(self, certificate):
|
||||
"""Create a new certificate.
|
||||
|
||||
:param certificate: a certificate within the request body.
|
||||
"""
|
||||
certificate_dict = certificate.as_dict()
|
||||
context = pecan.request.context
|
||||
bay = certificate.get_bay()
|
||||
policy.enforce(context, 'certificate:create', bay,
|
||||
action='certificate:create')
|
||||
certificate_dict = certificate.as_dict()
|
||||
certificate_dict['project_id'] = context.project_id
|
||||
certificate_dict['user_id'] = context.user_id
|
||||
cert_obj = objects.Certificate(context, **certificate_dict)
|
||||
|
||||
new_cert = pecan.request.rpcapi.sign_certificate(certificate.get_bay(),
|
||||
new_cert = pecan.request.rpcapi.sign_certificate(bay,
|
||||
cert_obj)
|
||||
return Certificate.convert_with_links(new_cert)
|
||||
|
|
|
@ -363,7 +363,6 @@ class ContainersController(rest.RestController):
|
|||
return Container.convert_with_links(res_container)
|
||||
|
||||
@expose.expose(Container, body=Container, status_code=201)
|
||||
@policy.enforce_wsgi("container", "create")
|
||||
@validation.enforce_bay_types('swarm')
|
||||
def post(self, container):
|
||||
"""Create a new container.
|
||||
|
@ -372,6 +371,9 @@ class ContainersController(rest.RestController):
|
|||
"""
|
||||
container_dict = container.as_dict()
|
||||
context = pecan.request.context
|
||||
bay = api_utils.get_resource('Bay', container_dict['bay_uuid'])
|
||||
policy.enforce(context, 'container:create', bay,
|
||||
action='container:create')
|
||||
container_dict['project_id'] = context.project_id
|
||||
container_dict['user_id'] = context.user_id
|
||||
new_container = objects.Container(context, **container_dict)
|
||||
|
@ -386,7 +388,6 @@ class ContainersController(rest.RestController):
|
|||
@wsme.validate(types.uuid, [ContainerPatchType])
|
||||
@expose.expose(Container, types.uuid_or_name,
|
||||
body=[ContainerPatchType])
|
||||
@policy.enforce_wsgi("container", "update")
|
||||
def patch(self, container_ident, patch):
|
||||
"""Update an existing container.
|
||||
|
||||
|
@ -395,6 +396,10 @@ class ContainersController(rest.RestController):
|
|||
"""
|
||||
container = api_utils.get_resource('Container',
|
||||
container_ident)
|
||||
context = pecan.request.context
|
||||
bay = api_utils.get_resource('Bay', container.bay_uuid)
|
||||
policy.enforce(context, 'container:update', bay,
|
||||
action='container:update')
|
||||
try:
|
||||
container_dict = container.as_dict()
|
||||
new_container = Container(**api_utils.apply_jsonpatch(
|
||||
|
@ -418,7 +423,6 @@ class ContainersController(rest.RestController):
|
|||
return Container.convert_with_links(container)
|
||||
|
||||
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||
@policy.enforce_wsgi("container")
|
||||
def delete(self, container_ident):
|
||||
"""Delete a container.
|
||||
|
||||
|
@ -426,5 +430,9 @@ class ContainersController(rest.RestController):
|
|||
"""
|
||||
container = api_utils.get_resource('Container',
|
||||
container_ident)
|
||||
context = pecan.request.context
|
||||
bay = api_utils.get_resource('Bay', container.bay_uuid)
|
||||
policy.enforce(context, 'container:delete', bay,
|
||||
action='container:delete')
|
||||
pecan.request.rpcapi.container_delete(container.uuid)
|
||||
container.destroy()
|
||||
|
|
|
@ -90,6 +90,8 @@ def enforce(context, rule=None, target=None,
|
|||
"""
|
||||
enforcer = init()
|
||||
credentials = context.to_dict()
|
||||
if not exc:
|
||||
exc = exception.PolicyNotAuthorized
|
||||
if target is None:
|
||||
target = {'project_id': context.project_id,
|
||||
'user_id': context.user_id}
|
||||
|
|
|
@ -17,7 +17,7 @@ from magnum.api.controllers.v1 import certificate as api_cert
|
|||
from magnum.common import utils
|
||||
from magnum.tests import base
|
||||
from magnum.tests.unit.api import base as api_base
|
||||
from magnum.tests.unit.api import utils as apiutils
|
||||
from magnum.tests.unit.api import utils as api_utils
|
||||
from magnum.tests.unit.objects import utils as obj_utils
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ class TestCertObject(base.TestCase):
|
|||
|
||||
@mock.patch('magnum.api.utils.get_resource')
|
||||
def test_cert_init(self, mock_get_resource):
|
||||
cert_dict = apiutils.cert_post_data()
|
||||
cert_dict = api_utils.cert_post_data()
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay.uuid = cert_dict['bay_uuid']
|
||||
mock_get_resource.return_value = mock_bay
|
||||
|
@ -50,7 +50,7 @@ class TestGetCertificate(api_base.FunctionalTest):
|
|||
self.addCleanup(conductor_api_patcher.stop)
|
||||
|
||||
def test_get_one(self):
|
||||
fake_cert = apiutils.cert_post_data()
|
||||
fake_cert = api_utils.cert_post_data()
|
||||
mock_cert = mock.MagicMock()
|
||||
mock_cert.as_dict.return_value = fake_cert
|
||||
self.conductor_api.get_ca_certificate.return_value = mock_cert
|
||||
|
@ -62,7 +62,7 @@ class TestGetCertificate(api_base.FunctionalTest):
|
|||
self.assertEqual(fake_cert['pem'], response['pem'])
|
||||
|
||||
def test_get_one_by_name(self):
|
||||
fake_cert = apiutils.cert_post_data()
|
||||
fake_cert = api_utils.cert_post_data()
|
||||
mock_cert = mock.MagicMock()
|
||||
mock_cert.as_dict.return_value = fake_cert
|
||||
self.conductor_api.get_ca_certificate.return_value = mock_cert
|
||||
|
@ -95,7 +95,7 @@ class TestGetCertificate(api_base.FunctionalTest):
|
|||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_links(self):
|
||||
fake_cert = apiutils.cert_post_data()
|
||||
fake_cert = api_utils.cert_post_data()
|
||||
mock_cert = mock.MagicMock()
|
||||
mock_cert.as_dict.return_value = fake_cert
|
||||
self.conductor_api.get_ca_certificate.return_value = mock_cert
|
||||
|
@ -130,7 +130,7 @@ class TestPost(api_base.FunctionalTest):
|
|||
return cert
|
||||
|
||||
def test_create_cert(self, ):
|
||||
new_cert = apiutils.cert_post_data(bay_uuid=self.bay.uuid)
|
||||
new_cert = api_utils.cert_post_data(bay_uuid=self.bay.uuid)
|
||||
del new_cert['pem']
|
||||
|
||||
response = self.post_json('/certificates', new_cert)
|
||||
|
@ -140,7 +140,7 @@ class TestPost(api_base.FunctionalTest):
|
|||
self.assertEqual('fake-pem', response.json['pem'])
|
||||
|
||||
def test_create_cert_by_bay_name(self, ):
|
||||
new_cert = apiutils.cert_post_data(bay_uuid=self.bay.name)
|
||||
new_cert = api_utils.cert_post_data(bay_uuid=self.bay.name)
|
||||
del new_cert['pem']
|
||||
|
||||
response = self.post_json('/certificates', new_cert)
|
||||
|
@ -151,7 +151,7 @@ class TestPost(api_base.FunctionalTest):
|
|||
self.assertEqual('fake-pem', response.json['pem'])
|
||||
|
||||
def test_create_cert_bay_not_found(self, ):
|
||||
new_cert = apiutils.cert_post_data(bay_uuid='not_found')
|
||||
new_cert = api_utils.cert_post_data(bay_uuid='not_found')
|
||||
del new_cert['pem']
|
||||
|
||||
response = self.post_json('/certificates', new_cert,
|
||||
|
@ -168,7 +168,7 @@ class TestCertPolicyEnforcement(api_base.FunctionalTest):
|
|||
super(TestCertPolicyEnforcement, self).setUp()
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({rule: "project:non_fake"})
|
||||
self.policy.set_rules({rule: "project_id:non_fake"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
@ -177,14 +177,15 @@ class TestCertPolicyEnforcement(api_base.FunctionalTest):
|
|||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
bay = obj_utils.create_test_bay(self.context)
|
||||
self._common_policy_check(
|
||||
"certificate:get", self.get_json,
|
||||
'/certificates/ce5da569-4f65-4272-9199-fac8c9fbc9d4',
|
||||
'/certificates/%s' % bay.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_create(self):
|
||||
bay = obj_utils.create_test_bay(self.context)
|
||||
cert = apiutils.cert_post_data(bay_uuid=bay.uuid)
|
||||
cert = api_utils.cert_post_data(bay_uuid=bay.uuid)
|
||||
self._common_policy_check(
|
||||
"certificate:create", self.post_json, '/certificates', cert,
|
||||
expect_errors=True)
|
||||
|
|
|
@ -635,7 +635,7 @@ class TestContainerController(api_base.FunctionalTest):
|
|||
class TestContainerEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({rule: 'project:non_fake'})
|
||||
self.policy.set_rules({rule: 'project_id:non_fake'})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
@ -662,21 +662,24 @@ class TestContainerEnforcement(api_base.FunctionalTest):
|
|||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_update(self):
|
||||
test_container = utils.get_test_container()
|
||||
container_uuid = test_container.get('uuid')
|
||||
bay = obj_utils.create_test_bay(self.context)
|
||||
container = obj_utils.create_test_container(self.context,
|
||||
bay_uuid=bay.uuid)
|
||||
params = [{'path': '/name',
|
||||
'value': 'new_name',
|
||||
'op': 'replace'}]
|
||||
self._common_policy_check(
|
||||
'container:update', self.app.patch_json,
|
||||
'/v1/containers/%s' % container_uuid, params,
|
||||
'/v1/containers/%s' % container.uuid, params,
|
||||
expect_errors=True)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_policy_disallow_create(self, mock_get_by_uuid):
|
||||
def test_policy_disallow_create(self):
|
||||
baymodel = obj_utils.create_test_baymodel(self.context)
|
||||
bay = obj_utils.create_test_bay(self.context,
|
||||
baymodel_id=baymodel.uuid)
|
||||
params = ('{"name": "My Docker", "image": "ubuntu",'
|
||||
'"command": "env", "memory": "512m",'
|
||||
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
|
||||
'"bay_uuid": "%s"}' % bay.uuid)
|
||||
|
||||
self._common_policy_check(
|
||||
'container:create', self.app.post, '/v1/containers', params=params,
|
||||
|
@ -684,7 +687,10 @@ class TestContainerEnforcement(api_base.FunctionalTest):
|
|||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_delete(self):
|
||||
bay = obj_utils.create_test_bay(self.context)
|
||||
container = obj_utils.create_test_container(self.context,
|
||||
bay_uuid=bay.uuid)
|
||||
self._common_policy_check(
|
||||
'container:delete', self.app.delete,
|
||||
'/v1/containers/%s' % comm_utils.generate_uuid(),
|
||||
'/v1/containers/%s' % container.uuid,
|
||||
expect_errors=True)
|
||||
|
|
|
@ -198,3 +198,27 @@ def get_test_magnum_service_object(context, **kw):
|
|||
for key in db_magnum_service:
|
||||
setattr(magnum_service, key, db_magnum_service[key])
|
||||
return magnum_service
|
||||
|
||||
|
||||
def create_test_container(context, **kw):
|
||||
"""Create and return a test container object.
|
||||
|
||||
Create a container in the DB and return a container object with
|
||||
appropriate attributes.
|
||||
"""
|
||||
container = get_test_container(context, **kw)
|
||||
container.create()
|
||||
return container
|
||||
|
||||
|
||||
def get_test_container(context, **kw):
|
||||
"""Return a test container object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_container = db_utils.get_test_container(**kw)
|
||||
container = objects.Container(context)
|
||||
for key in db_container:
|
||||
setattr(container, key, db_container[key])
|
||||
return container
|
||||
|
|
Loading…
Reference in New Issue