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

Daniel H Barboza danielhb at linux.vnet.ibm.com
Tue Feb 18 21:03:14 UTC 2014


Reviewed-by: Daniel Barboza <danielhb at linux.vnet.ibm.com>

On 02/18/2014 03:23 PM, Aline Manera wrote:
> 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):




More information about the Kimchi-devel mailing list