From: Aline Manera <alinefm(a)linux.vnet.ibm.com>
An user only can perform actions to a Resource if it is not protected or
if the logged user is listed in the resource users and groups parameters.
Also update the tests to reflect those changes.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/control/base.py | 28 ++++++++++++++++++++++------
src/kimchi/control/host.py | 2 +-
src/kimchi/exception.py | 4 ++++
src/kimchi/i18n.py | 1 +
tests/test_authorization.py | 12 ++++++++++--
tests/test_rest.py | 2 +-
6 files changed, 39 insertions(+), 10 deletions(-)
diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py
index 674c13b..ac24b3f 100644
--- a/src/kimchi/control/base.py
+++ b/src/kimchi/control/base.py
@@ -27,8 +27,8 @@
from kimchi.control.utils import parse_request, validate_method
from kimchi.control.utils import validate_params
from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import KimchiException
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
+from kimchi.exception import KimchiException, MissingParameter, NotFoundError
+from kimchi.exception import OperationFailed, UnauthorizedError
class Resource(object):
@@ -65,8 +65,14 @@ def _redirect(self, ident, code=303):
def generate_action_handler(self, action_name, action_args=None):
def wrapper(*args, **kwargs):
- validate_method(('POST'))
+ method = validate_method(('POST'),
+ self.role_key, self.admin_methods)
+
try:
+ self.lookup()
+ if not self.is_authorized():
+ raise UnauthorizedError('KCHAPI0009E')
+
model_args = list(self.model_args)
if action_args is not None:
request = parse_request()
@@ -87,10 +93,12 @@ def wrapper(*args, **kwargs):
raise cherrypy.HTTPError(400, e.message)
except InvalidOperation, e:
raise cherrypy.HTTPError(400, e.message)
- except OperationFailed, e:
- raise cherrypy.HTTPError(500, e.message)
+ except UnauthorizedError, e:
+ raise cherrypy.HTTPError(403, e.message)
except NotFoundError, e:
raise cherrypy.HTTPError(404, e.message)
+ except OperationFailed, e:
+ raise cherrypy.HTTPError(500, e.message)
except KimchiException, e:
raise cherrypy.HTTPError(500, e.message)
@@ -121,8 +129,14 @@ def delete(self):
@cherrypy.expose
def index(self):
- method = validate_method(('GET', 'DELETE', 'PUT'))
+ method = validate_method(('GET', 'DELETE', 'PUT'),
+ self.role_key, self.admin_methods)
+
try:
+ self.lookup()
+ if not self.is_authorized():
+ raise UnauthorizedError('KCHAPI0009E')
+
return {'GET': self.get,
'DELETE': self.delete,
'PUT': self.update}[method]()
@@ -130,6 +144,8 @@ def index(self):
raise cherrypy.HTTPError(400, e.message)
except InvalidParameter, e:
raise cherrypy.HTTPError(400, e.message)
+ except UnauthorizedError, e:
+ raise cherrypy.HTTPError(403, e.message)
except NotFoundError, e:
raise cherrypy.HTTPError(404, e.message)
except OperationFailed, e:
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
index e1971cc..c962472 100644
--- a/src/kimchi/control/host.py
+++ b/src/kimchi/control/host.py
@@ -42,7 +42,7 @@ def __init__(self, model, id=None):
@cherrypy.expose
def swupdate(self):
- validate_method(('POST'))
+ validate_method(('POST'), self.role_key, self.admin_methods)
try:
task = self.model.host_swupdate()
cherrypy.response.status = 202
diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py
index d84ddb9..9828223 100644
--- a/src/kimchi/exception.py
+++ b/src/kimchi/exception.py
@@ -92,3 +92,7 @@ class IsoFormatError(KimchiException):
class TimeoutExpired(KimchiException):
pass
+
+
+class UnauthorizedError(KimchiException):
+ pass
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 0c76145..be6c532 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -31,6 +31,7 @@
"KCHAPI0006E": _("Unable to parse JSON request"),
"KCHAPI0007E": _("This API only supports JSON"),
"KCHAPI0008E": _("Parameters does not match requirement in schema:
%(err)s"),
+ "KCHAPI0009E": _("You don't have permission to perform this
operation."),
"KCHASYNC0001E": _("Datastore is not initiated in the model
object."),
"KCHASYNC0002E": _("Unable to start task due error: %(err)s"),
diff --git a/tests/test_authorization.py b/tests/test_authorization.py
index 03f8a88..3d0b357 100644
--- a/tests/test_authorization.py
+++ b/tests/test_authorization.py
@@ -116,7 +116,15 @@ def test_nonroot_access(self):
self.assertEquals(200, resp.status)
resp = self.request('/vms', req, 'POST')
self.assertEquals(403, resp.status)
- resp = self.request('/vms', '{}', 'PUT')
+
+ # Create a vm using mockmodel directly to test Resource access
+ model.templates_create({'name': 'test', 'cdrom':
'/nonexistent.iso'})
+ model.vms_create({'name': 'test', 'template':
'/templates/test'})
+
+ resp = self.request('/vms/test', '{}', 'PUT')
self.assertEquals(403, resp.status)
- resp = self.request('/vms', '{}', 'DELETE')
+ resp = self.request('/vms/test', '{}', 'DELETE')
self.assertEquals(403, resp.status)
+
+ model.template_delete('test')
+ model.vm_delete('test')
diff --git a/tests/test_rest.py b/tests/test_rest.py
index 54209ef..3c8c537 100644
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -1207,7 +1207,7 @@ def verify_template(t, res):
# Test nonexistent fields, specify a field 'foo' isn't in the
Template
t['foo'] = "bar"
req = json.dumps(t)
- resp = self.request('/templates/%s' % oldname, req, 'PUT')
+ resp = self.request('/templates/%s' % tmpl_name, req, 'PUT')
self.assertEquals(400, resp.status)
# Delete the template
--
1.9.3