Merge "Enable api v2 policy check"
This commit is contained in:
commit
8adf4b9955
@ -25,6 +25,7 @@ from cyborg.api.controllers import link
|
|||||||
from cyborg.api.controllers import types
|
from cyborg.api.controllers import types
|
||||||
from cyborg.api import expose
|
from cyborg.api import expose
|
||||||
from cyborg.common import exception
|
from cyborg.common import exception
|
||||||
|
from cyborg.common import policy
|
||||||
from cyborg import objects
|
from cyborg import objects
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -107,7 +108,7 @@ class ARQsController(base.CyborgController):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:arq", "create", False)
|
@policy.authorize_wsgi("cyborg:arq", "create", False)
|
||||||
@expose.expose(ARQCollection, body=types.jsontype,
|
@expose.expose(ARQCollection, body=types.jsontype,
|
||||||
status_code=http_client.CREATED)
|
status_code=http_client.CREATED)
|
||||||
def post(self, req):
|
def post(self, req):
|
||||||
@ -159,7 +160,7 @@ class ARQsController(base.CyborgController):
|
|||||||
LOG.info('[arqs] post returned: %s', ret)
|
LOG.info('[arqs] post returned: %s', ret)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:arq", "get_one")
|
@policy.authorize_wsgi("cyborg:arq", "get_one")
|
||||||
@expose.expose(ARQ, wtypes.text)
|
@expose.expose(ARQ, wtypes.text)
|
||||||
def get_one(self, uuid):
|
def get_one(self, uuid):
|
||||||
"""Get a single ARQ by UUID."""
|
"""Get a single ARQ by UUID."""
|
||||||
@ -167,7 +168,7 @@ class ARQsController(base.CyborgController):
|
|||||||
extarq = objects.ExtARQ.get(context, uuid)
|
extarq = objects.ExtARQ.get(context, uuid)
|
||||||
return ARQ.convert_with_links(extarq.arq)
|
return ARQ.convert_with_links(extarq.arq)
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:arq", "get_all")
|
@policy.authorize_wsgi("cyborg:arq", "get_all", False)
|
||||||
@expose.expose(ARQCollection, wtypes.text, types.uuid)
|
@expose.expose(ARQCollection, wtypes.text, types.uuid)
|
||||||
def get_all(self, bind_state=None, instance=None):
|
def get_all(self, bind_state=None, instance=None):
|
||||||
"""Retrieve a list of arqs."""
|
"""Retrieve a list of arqs."""
|
||||||
@ -203,7 +204,7 @@ class ARQsController(base.CyborgController):
|
|||||||
LOG.info('[arqs:get_all] Returned: %s', ret)
|
LOG.info('[arqs:get_all] Returned: %s', ret)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:arq", "delete")
|
@policy.authorize_wsgi("cyborg:arq", "delete")
|
||||||
@expose.expose(None, wtypes.text, wtypes.text,
|
@expose.expose(None, wtypes.text, wtypes.text,
|
||||||
status_code=http_client.NO_CONTENT)
|
status_code=http_client.NO_CONTENT)
|
||||||
def delete(self, arqs=None, instance=None):
|
def delete(self, arqs=None, instance=None):
|
||||||
@ -260,7 +261,7 @@ class ARQsController(base.CyborgController):
|
|||||||
raise exception.PatchError(reason=reason)
|
raise exception.PatchError(reason=reason)
|
||||||
return valid_fields
|
return valid_fields
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:arq", "update")
|
@policy.authorize_wsgi("cyborg:arq", "update", False)
|
||||||
@expose.expose(None, body=types.jsontype,
|
@expose.expose(None, body=types.jsontype,
|
||||||
status_code=http_client.ACCEPTED)
|
status_code=http_client.ACCEPTED)
|
||||||
def patch(self, patch_list):
|
def patch(self, patch_list):
|
||||||
|
@ -27,6 +27,7 @@ from cyborg.api.controllers import link
|
|||||||
from cyborg.api.controllers import types
|
from cyborg.api.controllers import types
|
||||||
from cyborg.api import expose
|
from cyborg.api import expose
|
||||||
from cyborg.common import exception
|
from cyborg.common import exception
|
||||||
|
from cyborg.common import policy
|
||||||
from cyborg import objects
|
from cyborg import objects
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -84,7 +85,7 @@ class DeviceProfileCollection(object):
|
|||||||
class DeviceProfilesController(base.CyborgController):
|
class DeviceProfilesController(base.CyborgController):
|
||||||
"""REST controller for Device Profiles."""
|
"""REST controller for Device Profiles."""
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:device_profile", "create", False)
|
@policy.authorize_wsgi("cyborg:device_profile", "create", False)
|
||||||
@expose.expose('json', body=types.jsontype,
|
@expose.expose('json', body=types.jsontype,
|
||||||
status_code=http_client.CREATED)
|
status_code=http_client.CREATED)
|
||||||
def post(self, req_devprof_list):
|
def post(self, req_devprof_list):
|
||||||
@ -164,7 +165,7 @@ class DeviceProfilesController(base.CyborgController):
|
|||||||
|
|
||||||
return api_obj_devprofs
|
return api_obj_devprofs
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:device_profile", "get_all")
|
@policy.authorize_wsgi("cyborg:device_profile", "get_all", False)
|
||||||
@expose.expose('json', wtypes.text)
|
@expose.expose('json', wtypes.text)
|
||||||
def get_all(self, name=None):
|
def get_all(self, name=None):
|
||||||
"""Retrieve a list of device profiles."""
|
"""Retrieve a list of device profiles."""
|
||||||
@ -181,7 +182,7 @@ class DeviceProfilesController(base.CyborgController):
|
|||||||
return wsme.api.Response(ret, status_code=http_client.OK,
|
return wsme.api.Response(ret, status_code=http_client.OK,
|
||||||
return_type=wsme.types.DictType)
|
return_type=wsme.types.DictType)
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:device_profile", "get_one")
|
@policy.authorize_wsgi("cyborg:device_profile", "get_one")
|
||||||
@expose.expose('json', wtypes.text)
|
@expose.expose('json', wtypes.text)
|
||||||
def get_one(self, uuid):
|
def get_one(self, uuid):
|
||||||
"""Retrieve a single device profile by uuid."""
|
"""Retrieve a single device profile by uuid."""
|
||||||
@ -202,14 +203,14 @@ class DeviceProfilesController(base.CyborgController):
|
|||||||
return wsme.api.Response(ret, status_code=http_client.OK,
|
return wsme.api.Response(ret, status_code=http_client.OK,
|
||||||
return_type=wsme.types.DictType)
|
return_type=wsme.types.DictType)
|
||||||
|
|
||||||
# @policy.authorize_wsgi("cyborg:device_profile", "delete")
|
@policy.authorize_wsgi("cyborg:device_profile", "delete")
|
||||||
@expose.expose(None, wtypes.text, status_code=http_client.NO_CONTENT)
|
@expose.expose(None, wtypes.text, status_code=http_client.NO_CONTENT)
|
||||||
def delete(self, value):
|
def delete(self, value):
|
||||||
"""Delete one or more device_profiles.
|
"""Delete one or more device_profiles.
|
||||||
|
|
||||||
URL: /device_profiles/{uuid} OR /device_profiles?value=foo,bar
|
URL: /device_profiles/{uuid} OR /device_profiles?value=foo,bar
|
||||||
|
|
||||||
:param value: Tis should be one of these two:
|
:param value: This should be one of these two:
|
||||||
- UUID of a device_profile.
|
- UUID of a device_profile.
|
||||||
- Comma-delimited list of device profile names.
|
- Comma-delimited list of device profile names.
|
||||||
"""
|
"""
|
||||||
|
@ -73,40 +73,37 @@ default_policies = [
|
|||||||
# All of these may be overridden by configuration, but we can
|
# All of these may be overridden by configuration, but we can
|
||||||
# depend on their existence throughout the code.
|
# depend on their existence throughout the code.
|
||||||
|
|
||||||
accelerator_policies = [
|
accelerator_request_policies = [
|
||||||
policy.RuleDefault('cyborg:accelerator:get',
|
policy.RuleDefault('cyborg:arq:get_all',
|
||||||
'rule:default',
|
'rule:default',
|
||||||
description='Retrieve accelerator records'),
|
description='Retrieve accelerator request records.'),
|
||||||
policy.RuleDefault('cyborg:accelerator:create',
|
policy.RuleDefault('cyborg:arq:get_one',
|
||||||
|
'rule:default',
|
||||||
|
description='Get an accelerator request record.'),
|
||||||
|
policy.RuleDefault('cyborg:arq:create',
|
||||||
'rule:allow',
|
'rule:allow',
|
||||||
description='Create accelerator records'),
|
description='Create accelerator request records.'),
|
||||||
policy.RuleDefault('cyborg:accelerator:delete',
|
policy.RuleDefault('cyborg:arq:delete',
|
||||||
'rule:default',
|
'rule:default',
|
||||||
description='Delete accelerator records'),
|
description='Delete accelerator request records.'),
|
||||||
policy.RuleDefault('cyborg:accelerator:update',
|
policy.RuleDefault('cyborg:arq:update',
|
||||||
'rule:default',
|
'rule:default',
|
||||||
description='Update accelerator records'),
|
description='Update accelerator request records.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
deployable_policies = [
|
device_profile_policies = [
|
||||||
policy.RuleDefault('cyborg:deployable:get_one',
|
policy.RuleDefault('cyborg:device_profile:get_all',
|
||||||
'rule:allow',
|
'rule:default',
|
||||||
description='Show deployable detail'),
|
description='Retrieve device_profile records.'),
|
||||||
policy.RuleDefault('cyborg:deployable:get_all',
|
policy.RuleDefault('cyborg:device_profile:get_one',
|
||||||
'rule:allow',
|
'rule:default',
|
||||||
description='Retrieve all deployable records'),
|
description='Get a device_profile record.'),
|
||||||
policy.RuleDefault('cyborg:deployable:create',
|
policy.RuleDefault('cyborg:device_profile:create',
|
||||||
'rule:admin_api',
|
'rule:is_admin',
|
||||||
description='Create deployable records'),
|
description='Create device_profile records.'),
|
||||||
policy.RuleDefault('cyborg:deployable:delete',
|
policy.RuleDefault('cyborg:device_profile:delete',
|
||||||
'rule:admin_api',
|
'rule:default',
|
||||||
description='Delete deployable records'),
|
description='Delete device_profile records.'),
|
||||||
policy.RuleDefault('cyborg:deployable:update',
|
|
||||||
'rule:admin_api',
|
|
||||||
description='Update deployable records'),
|
|
||||||
policy.RuleDefault('cyborg:deployable:program',
|
|
||||||
'rule:allow',
|
|
||||||
description='Program deployable(FPGA) records'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
fpga_policies = [
|
fpga_policies = [
|
||||||
@ -124,9 +121,9 @@ fpga_policies = [
|
|||||||
|
|
||||||
def list_policies():
|
def list_policies():
|
||||||
return default_policies \
|
return default_policies \
|
||||||
+ accelerator_policies \
|
+ fpga_policies \
|
||||||
+ deployable_policies \
|
+ accelerator_request_policies \
|
||||||
+ fpga_policies
|
+ device_profile_policies
|
||||||
|
|
||||||
|
|
||||||
@lockutils.synchronized('policy_enforcer', 'cyborg-')
|
@lockutils.synchronized('policy_enforcer', 'cyborg-')
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"""Base classes for API tests."""
|
"""Base classes for API tests."""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_context import context
|
||||||
import pecan
|
import pecan
|
||||||
import pecan.testing
|
import pecan.testing
|
||||||
|
|
||||||
@ -107,6 +108,10 @@ class BaseApiTest(base.DbTestCase):
|
|||||||
headers=headers, extra_environ=extra_environ,
|
headers=headers, extra_environ=extra_environ,
|
||||||
status=status, method="post")
|
status=status, method="post")
|
||||||
|
|
||||||
|
def gen_context(self, value, **kwargs):
|
||||||
|
ct = context.RequestContext.from_dict(value, **kwargs)
|
||||||
|
return ct
|
||||||
|
|
||||||
def gen_headers(self, context, **kw):
|
def gen_headers(self, context, **kw):
|
||||||
"""Generate a header for a simulated HTTP request to Pecan test app.
|
"""Generate a header for a simulated HTTP request to Pecan test app.
|
||||||
|
|
||||||
@ -119,6 +124,10 @@ class BaseApiTest(base.DbTestCase):
|
|||||||
"""
|
"""
|
||||||
ct = context.to_dict()
|
ct = context.to_dict()
|
||||||
ct.update(kw)
|
ct.update(kw)
|
||||||
|
if ct.get("is_admin"):
|
||||||
|
role = "admin"
|
||||||
|
else:
|
||||||
|
role = "user"
|
||||||
headers = {
|
headers = {
|
||||||
'X-User-Name': ct.get("user_name") or "user",
|
'X-User-Name': ct.get("user_name") or "user",
|
||||||
'X-User-Id':
|
'X-User-Id':
|
||||||
@ -131,9 +140,8 @@ class BaseApiTest(base.DbTestCase):
|
|||||||
'X-User-Domain-Name': ct.get("domain_name") or "no_domain",
|
'X-User-Domain-Name': ct.get("domain_name") or "no_domain",
|
||||||
'X-Auth-Token':
|
'X-Auth-Token':
|
||||||
ct.get("auth_token") or "b9764005b8c145bf972634fb16a826e8",
|
ct.get("auth_token") or "b9764005b8c145bf972634fb16a826e8",
|
||||||
'X-Roles': ct.get("roles") or "cyborg"
|
'X-Roles': ct.get("roles") or role
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
def get_json(self, path, expect_errors=False, headers=None,
|
def get_json(self, path, expect_errors=False, headers=None,
|
||||||
|
@ -111,3 +111,19 @@ class TestARQsController(v2_test.APITestV2):
|
|||||||
args = '?' + "instance=" + instance
|
args = '?' + "instance=" + instance
|
||||||
response = self.delete(url + args, headers=self.headers)
|
response = self.delete(url + args, headers=self.headers)
|
||||||
self.assertEqual(http_client.NO_CONTENT, response.status_int)
|
self.assertEqual(http_client.NO_CONTENT, response.status_int)
|
||||||
|
|
||||||
|
def test_delete_with_non_default(self):
|
||||||
|
value = {"is_admin": False, "roles": "user", "is_admin_project": False}
|
||||||
|
ct = self.gen_context(value)
|
||||||
|
headers = self.gen_headers(ct)
|
||||||
|
url = self.ARQ_URL
|
||||||
|
arq = self.fake_extarqs[0].arq
|
||||||
|
args = '?' + "arqs=" + str(arq['uuid'])
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
self.delete(url + args, headers=headers)
|
||||||
|
except Exception as e:
|
||||||
|
exc = e
|
||||||
|
# Cyborg does not raise different exception when policy check failed
|
||||||
|
# now, improve this case with assertRaises later.
|
||||||
|
self.assertIn("Bad response: 403 Forbidden", exc.args[0])
|
||||||
|
@ -23,7 +23,6 @@ from cyborg.tests.unit import fake_device_profile
|
|||||||
|
|
||||||
|
|
||||||
class TestDeviceProfileController(v2_test.APITestV2):
|
class TestDeviceProfileController(v2_test.APITestV2):
|
||||||
|
|
||||||
DP_URL = '/device_profiles'
|
DP_URL = '/device_profiles'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -72,6 +71,20 @@ class TestDeviceProfileController(v2_test.APITestV2):
|
|||||||
for in_dp, out_dp in zip(self.fake_dp_objs, out_dps):
|
for in_dp, out_dp in zip(self.fake_dp_objs, out_dps):
|
||||||
self._validate_dp(in_dp, out_dp)
|
self._validate_dp(in_dp, out_dp)
|
||||||
|
|
||||||
|
def test_create_with_non_admin(self):
|
||||||
|
value = {"is_admin": False, "roles": "user", "is_admin_project": False}
|
||||||
|
ct = self.gen_context(value)
|
||||||
|
headers = self.gen_headers(ct)
|
||||||
|
dp = [self.fake_dps[0]]
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
self.post_json(self.DP_URL, dp, headers=headers)
|
||||||
|
except Exception as e:
|
||||||
|
exc = e
|
||||||
|
# Cyborg does not raise different exception when policy check failed
|
||||||
|
# now, improve this case with assertRaises later.
|
||||||
|
self.assertIn("Bad response: 403 Forbidden", exc.args[0])
|
||||||
|
|
||||||
@mock.patch('cyborg.objects.DeviceProfile.create')
|
@mock.patch('cyborg.objects.DeviceProfile.create')
|
||||||
def test_create(self, mock_obj_dp):
|
def test_create(self, mock_obj_dp):
|
||||||
dp = [self.fake_dps[0]]
|
dp = [self.fake_dps[0]]
|
||||||
@ -92,3 +105,18 @@ class TestDeviceProfileController(v2_test.APITestV2):
|
|||||||
url = self.DP_URL + "?value=mydp"
|
url = self.DP_URL + "?value=mydp"
|
||||||
response = self.delete(url, headers=self.headers)
|
response = self.delete(url, headers=self.headers)
|
||||||
self.assertEqual(http_client.NO_CONTENT, response.status_int)
|
self.assertEqual(http_client.NO_CONTENT, response.status_int)
|
||||||
|
|
||||||
|
def test_delete_with_non_default(self):
|
||||||
|
value = {"is_admin": False, "roles": "user", "is_admin_project": False}
|
||||||
|
ct = self.gen_context(value)
|
||||||
|
headers = self.gen_headers(ct)
|
||||||
|
dp = self.fake_dp_objs[0]
|
||||||
|
url = self.DP_URL + '/%s'
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
self.delete(url % dp['uuid'], headers=headers)
|
||||||
|
except Exception as e:
|
||||||
|
exc = e
|
||||||
|
# Cyborg does not raise different exception when policy check failed
|
||||||
|
# now, improve this case with assertRaises later.
|
||||||
|
self.assertIn("Bad response: 403 Forbidden", exc.args[0])
|
||||||
|
Loading…
Reference in New Issue
Block a user