[PATCH] [Wok 0/3] Allow protecting an resource action (POST) when resource (GET) is not protected

This patch also includes test cases and fix some issues found during the test cases creation. Aline Manera (3): Do not sort tests on Wok Specify user when doing a request instead of trying to override FakeUser class attribute Allow protecting an resource action (POST) when resource (GET) is not protected src/wok/control/base.py | 13 +++++++---- src/wok/control/config.py | 6 ++--- tests/run_tests.sh.in | 16 ++----------- tests/test_authorization.py | 57 +++++++++++++++++++++++++++++++++++++++++++++ tests/utils.py | 18 ++++++-------- 5 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 tests/test_authorization.py -- 2.9.3

Wok does not have model vs mock model tests so there is no need to sort them. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/run_tests.sh.in | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/run_tests.sh.in b/tests/run_tests.sh.in index a259bda..64e6e49 100644 --- a/tests/run_tests.sh.in +++ b/tests/run_tests.sh.in @@ -2,7 +2,7 @@ # # Project Wok # -# Copyright IBM Corp, 2013-2016 +# Copyright IBM Corp, 2013-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -40,16 +40,4 @@ else CMD="python -m unittest" fi -LIST=($ARGS) -MODEL_LIST=() -MOCK_LIST=() -for ((i=0;i<${#LIST[@]};i++)); do - - if [[ ${LIST[$i]} == test_model* ]]; then - MODEL_LIST+=(${LIST[$i]}) - else - MOCK_LIST+=(${LIST[$i]}) - fi -done - -PYTHONPATH=../src:../ $CMD $OPTS ${MODEL_LIST[@]} ${MOCK_LIST[@]} +PYTHONPATH=../src:../ $CMD $OPTS $ARGS -- 2.9.3

A class attribute (sudo) was added to FakeUser class to allow request with different type of access. But it will not work when FakeUser class instance is already in memory and login() function only calls a static method to authenticate user, ie, the class attribute will be the original one. To avoid problems on that area, specify which user to do a request. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/utils.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 9c18637..3c7e9da 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -42,7 +42,7 @@ from wok.utils import wok_log HOST = '0.0.0.0' PROXY_PORT = 8001 -fake_user = {'root': 'letmein!'} +fake_user = {'admin': 'letmein!', 'user': 'letmein!'} def get_fake_user(): @@ -109,19 +109,19 @@ def running_as_root(): return os.geteuid() == 0 -def _request(conn, path, data, method, headers): +def _request(conn, path, data, method, headers, user): if headers is None: headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} if 'AUTHORIZATION' not in headers.keys(): - user, pw = fake_user.items()[0] + user, pw = user, fake_user[user] hdr = "Basic " + base64.b64encode("%s:%s" % (user, pw)) headers['AUTHORIZATION'] = hdr conn.request(method, path, data, headers) return conn.getresponse() -def request(path, data=None, method='GET', headers=None): +def request(path, data=None, method='GET', headers=None, user='admin'): # verify if HTTPSConnection has context parameter if "context" in inspect.getargspec(httplib.HTTPSConnection.__init__).args: context = ssl._create_unverified_context() @@ -129,12 +129,11 @@ def request(path, data=None, method='GET', headers=None): else: conn = httplib.HTTPSConnection(HOST, PROXY_PORT) - return _request(conn, path, data, method, headers) + return _request(conn, path, data, method, headers, user) class FakeUser(User): auth_type = "fake" - sudo = True def __init__(self, username): super(FakeUser, self).__init__(username) @@ -143,9 +142,7 @@ class FakeUser(User): return sorted([group.gr_name for group in grp.getgrall()])[0:3] def _get_role(self): - if self.sudo: - return 'admin' - return 'user' + return self.name @staticmethod def authenticate(username, password, service="passwd"): @@ -156,13 +153,12 @@ class FakeUser(User): 'code': e.message}) -def patch_auth(sudo=True): +def patch_auth(): """ Override the authenticate function with a simple test against an internal dict of users and passwords. """ config.set("authentication", "method", "fake") - FakeUser.sudo = sudo def wait_task(task_lookup, taskid, timeout=10): -- 2.9.3

Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/wok/control/base.py | 13 +++++++---- src/wok/control/config.py | 6 ++--- tests/test_authorization.py | 57 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 tests/test_authorization.py diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 3070e53..0791062 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -27,7 +27,7 @@ import urllib2 import wok.template from wok.asynctask import save_request_log_id -from wok.auth import USER_GROUPS, USER_NAME, USER_ROLE +from wok.auth import wokauth, USER_GROUPS, USER_NAME, USER_ROLE from wok.control.utils import get_class_name, internal_redirect, model_fn from wok.control.utils import parse_request, validate_method from wok.control.utils import validate_params @@ -91,7 +91,7 @@ class Resource(object): raise cherrypy.HTTPRedirect(base_uri % tuple(uri_params), code) def generate_action_handler(self, action_name, action_args=None, - destructive=False): + destructive=False, protected=None): def _render_element(self, ident): self._redirect(ident) uri_params = [] @@ -104,7 +104,8 @@ class Resource(object): return self._generate_action_handler_base(action_name, _render_element, destructive=destructive, - action_args=action_args) + action_args=action_args, + protected=protected) def generate_action_handler_task(self, action_name, action_args=None): def _render_task(self, task): @@ -115,10 +116,14 @@ class Resource(object): action_args=action_args) def _generate_action_handler_base(self, action_name, render_fn, - destructive=False, action_args=None): + destructive=False, action_args=None, + protected=None): def wrapper(*args, **kwargs): # status must be always set in order to request be logged. # use 500 as fallback for "exception not handled" cases. + if protected is not None and protected: + wokauth() + details = None status = 500 diff --git a/src/wok/control/config.py b/src/wok/control/config.py index 8da2fc0..a18fff0 100644 --- a/src/wok/control/config.py +++ b/src/wok/control/config.py @@ -44,7 +44,7 @@ class Config(Resource): self.admin_methods = ['POST'] self.plugins = Plugins(self.model) self.log_map = CONFIG_REQUESTS - self.reload = self.generate_action_handler('reload') + self.reload = self.generate_action_handler('reload', protected=True) @property def data(self): @@ -64,8 +64,8 @@ class Plugin(Resource): self.admin_methods = ['POST'] self.uri_fmt = "/config/plugins/%s" self.log_map = PLUGIN_REQUESTS - self.enable = self.generate_action_handler('enable') - self.disable = self.generate_action_handler('disable') + self.enable = self.generate_action_handler('enable', protected=True) + self.disable = self.generate_action_handler('disable', protected=True) @property def data(self): diff --git a/tests/test_authorization.py b/tests/test_authorization.py new file mode 100644 index 0000000..7b7bbcc --- /dev/null +++ b/tests/test_authorization.py @@ -0,0 +1,57 @@ +# +# Project Wok +# +# Copyright IBM Corp, 2014-2017 +# +# 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 unittest +from functools import partial + +from tests.utils import patch_auth +from tests.utils import request, run_server + +test_server = None + + +def setUpModule(): + global test_server + + patch_auth() + test_server = run_server(test_mode=True) + + +def tearDownModule(): + test_server.stop() + + +class AuthorizationTests(unittest.TestCase): + def setUp(self): + self.request = partial(request, user='user') + + def test_nonroot_access(self): + # Non-root users can not reload wok config + resp = self.request('/config', '{}', 'GET') + self.assertEquals(200, resp.status) + resp = self.request('/config/reload', '{}', 'POST') + self.assertEquals(403, resp.status) + + # Non-root users can not enable/disable a plugin + resp = self.request('/config/plugins/sample', '{}', 'GET') + self.assertEquals(200, resp.status) + resp = self.request('/config/plugins/sample/enable', '{}', 'POST') + self.assertEquals(403, resp.status) + resp = self.request('/config/plugins/sample/disable', '{}', 'POST') + self.assertEquals(403, resp.status) -- 2.9.3

Reviewed-By: Lucio Correia <luciojhc@linux.vnet.ibm.com> On 16/02/2017 15:20, Aline Manera wrote:
This patch also includes test cases and fix some issues found during the test cases creation.
Aline Manera (3): Do not sort tests on Wok Specify user when doing a request instead of trying to override FakeUser class attribute Allow protecting an resource action (POST) when resource (GET) is not protected
src/wok/control/base.py | 13 +++++++---- src/wok/control/config.py | 6 ++--- tests/run_tests.sh.in | 16 ++----------- tests/test_authorization.py | 57 +++++++++++++++++++++++++++++++++++++++++++++ tests/utils.py | 18 ++++++-------- 5 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 tests/test_authorization.py
-- Lucio Correia

Applied. Thanks. Regards, Aline Manera
participants (2)
-
Aline Manera
-
Lucio Correia