[Kimchi-devel] [PATCH] Block access for non-root users

Aline Manera alinefm at linux.vnet.ibm.com
Tue Feb 18 18:23:52 UTC 2014


From: Aline Manera <alinefm at br.ibm.com>

Non-root users must have restricted access to Kimchi.
This patch block non-root urser to:
- get or create debug reports;
- reboot or shutdown host system;
- create, activate/deactivate or delete networks;
- create, activate/deactivate or delete storage pools;
- update or delete templates;
- create, start/stop/connect or delete vms.

It also updates the tests cases to always run as a root user.
And add authorization tests to make sure non-root users have restricted
access to kimchi.

Signed-off-by: Aline Manera <alinefm at br.ibm.com>
---
 src/kimchi/control/debugreports.py |    2 +-
 src/kimchi/control/host.py         |    2 +-
 src/kimchi/control/networks.py     |    2 +-
 src/kimchi/control/storagepools.py |    2 +-
 src/kimchi/control/templates.py    |    2 +-
 src/kimchi/control/vms.py          |    2 +-
 tests/Makefile.am                  |    1 +
 tests/test_authorization.py        |  124 ++++++++++++++++++++++++++++++++++++
 tests/utils.py                     |   23 ++++++-
 9 files changed, 153 insertions(+), 7 deletions(-)
 create mode 100644 tests/test_authorization.py

diff --git a/src/kimchi/control/debugreports.py b/src/kimchi/control/debugreports.py
index 324d826..57dc0f3 100644
--- a/src/kimchi/control/debugreports.py
+++ b/src/kimchi/control/debugreports.py
@@ -26,7 +26,7 @@ from kimchi.control.utils import internal_redirect
 from kimchi.control.utils import UrlSubNode
 
 
- at UrlSubNode("debugreports", True)
+ at UrlSubNode("debugreports", True, ['GET', 'POST'])
 class DebugReports(AsyncCollection):
     def __init__(self, model):
         super(DebugReports, self).__init__(model)
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
index 0852bd0..41e0040 100644
--- a/src/kimchi/control/host.py
+++ b/src/kimchi/control/host.py
@@ -31,7 +31,7 @@ from kimchi.exception import OperationFailed
 from kimchi.template import render
 
 
- at UrlSubNode("host", True)
+ at UrlSubNode("host", True, ['POST'])
 class Host(Resource):
     def __init__(self, model, id=None):
         super(Host, self).__init__(model, id)
diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py
index 8510e49..3a02f60 100644
--- a/src/kimchi/control/networks.py
+++ b/src/kimchi/control/networks.py
@@ -25,7 +25,7 @@ from kimchi.control.base import Collection, Resource
 from kimchi.control.utils import UrlSubNode
 
 
- at UrlSubNode("networks", True)
+ at UrlSubNode("networks", True, ['POST', 'DELETE'])
 class Networks(Collection):
     def __init__(self, model):
         super(Networks, self).__init__(model)
diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py
index ea19609..7e6bdd7 100644
--- a/src/kimchi/control/storagepools.py
+++ b/src/kimchi/control/storagepools.py
@@ -34,7 +34,7 @@ from kimchi.model.storagepools import ISO_POOL_NAME
 from kimchi.control.utils import UrlSubNode
 
 
- at UrlSubNode("storagepools", True)
+ at UrlSubNode("storagepools", True, ['POST', 'DELETE'])
 class StoragePools(Collection):
     def __init__(self, model):
         super(StoragePools, self).__init__(model)
diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py
index 58dafcc..8135e32 100644
--- a/src/kimchi/control/templates.py
+++ b/src/kimchi/control/templates.py
@@ -25,7 +25,7 @@ from kimchi.control.base import Collection, Resource
 from kimchi.control.utils import UrlSubNode
 
 
- at UrlSubNode("templates", True)
+ at UrlSubNode("templates", True, ['PUT', 'DELETE'])
 class Templates(Collection):
     def __init__(self, model):
         super(Templates, self).__init__(model)
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 60fc8ff..a74ce27 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -27,7 +27,7 @@ from kimchi.control.utils import internal_redirect, UrlSubNode
 from kimchi.control.vm import sub_nodes
 
 
- at UrlSubNode("vms", True)
+ at UrlSubNode("vms", True, ['POST', 'PUT', 'DELETE'])
 class VMs(Collection):
     def __init__(self, model):
         super(VMs, self).__init__(model)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0487ffc..e8db05c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,6 +24,7 @@ EXTRA_DIST = \
 	Makefile.am \
 	run_tests.sh.in \
 	iso_gen.py   \
+	test_authorization.py \
 	test_config.py.in \
 	test_exception.py \
 	test_mockmodel.py \
diff --git a/tests/test_authorization.py b/tests/test_authorization.py
new file mode 100644
index 0000000..7f939a8
--- /dev/null
+++ b/tests/test_authorization.py
@@ -0,0 +1,124 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import json
+import os
+import unittest
+
+
+from functools import partial
+
+
+import kimchi.mockmodel
+from utils import get_free_port, patch_auth, request
+from utils import run_server
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+
+
+def setUpModule():
+    global test_server, model, host, port, ssl_port
+
+    patch_auth(sudo = False)
+    model = kimchi.mockmodel.MockModel('/tmp/obj-store-test')
+    host = '127.0.0.1'
+    port = get_free_port('http')
+    test_server = run_server(host, port, None, test_mode=True, model=model)
+
+
+def tearDownModule():
+    test_server.stop()
+    os.unlink('/tmp/obj-store-test')
+
+
+class AuthorizationTests(unittest.TestCase):
+    def setUp(self):
+        self.request = partial(request, host, port)
+        model.reset()
+
+    def test_nonroot_access(self):
+        # Non-root users can access static host information
+        resp = self.request('/host', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+
+        # Non-root users can access host stats
+        resp = self.request('/host/stats', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+
+        # Non-root users can not reboot/shutdown host system
+        resp = self.request('/host/reboot', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/host/shutdown', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+
+        # Non-root users can not get or debug reports
+        resp = self.request('/debugreports', '{}', 'GET')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/debugreports', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+
+        # Non-root users can not create or delete network (only get)
+        resp = self.request('/networks', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+        resp = self.request('/networks', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/networks/default/activate', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/networks/default', '{}', 'DELETE')
+        self.assertEquals(401, resp.status)
+
+        # Non-root users can not create or delete storage pool (only get)
+        resp = self.request('/storagepools', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+        resp = self.request('/storagepools', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/storagepools/default/activate', '{}', 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/storagepools/default', '{}', 'DELETE')
+        self.assertEquals(401, resp.status)
+
+        # Non-root users can not update or delete a template
+        # but he can get and create a new one
+        resp = self.request('/templates', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+        req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'})
+        resp = self.request('/templates', req, 'POST')
+        self.assertEquals(201, resp.status)
+        resp = self.request('/templates/test', '{}', 'PUT')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/templates/test', '{}', 'DELETE')
+        self.assertEquals(401, resp.status)
+
+        # Non-root users can only get vms
+        resp = self.request('/vms', '{}', 'GET')
+        self.assertEquals(200, resp.status)
+        resp = self.request('/vms', req, 'POST')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/vms', '{}', 'PUT')
+        self.assertEquals(401, resp.status)
+        resp = self.request('/vms', '{}', 'DELETE')
+        self.assertEquals(401, resp.status)
diff --git a/tests/utils.py b/tests/utils.py
index 14c57d4..18b707c 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -147,11 +147,31 @@ def https_request(host, port, path, data=None, method='GET', headers=None):
     return _request(conn, path, data, method, headers)
 
 
-def patch_auth():
+def patch_auth(sudo=True):
     """
     Override the authenticate function with a simple test against an
     internal dict of users and passwords.
     """
+    USER_ID = 'userid'
+    USER_GROUPS = 'groups'
+    USER_SUDO = 'sudo'
+
+    class _User(object):
+        def __init__(self, userid):
+            self.user = {}
+            self.user[USER_ID] = userid
+            self.user[USER_GROUPS] = None
+            self.user[USER_SUDO] = sudo
+
+        def get_groups(self):
+            return self.user[USER_GROUPS]
+
+        def has_sudo(self):
+            return self.user[USER_SUDO]
+
+        def get_user(self):
+            return self.user
+
     def _authenticate(username, password, service="passwd"):
         try:
             return fake_user[username] == password
@@ -161,6 +181,7 @@ def patch_auth():
 
     import kimchi.auth
     kimchi.auth.authenticate = _authenticate
+    kimchi.auth.User = _User
 
 
 def normalize_xml(xml_str):
-- 
1.7.10.4




More information about the Kimchi-devel mailing list