Reviewed-by: Daniel Barboza <danielhb(a)linux.vnet.ibm.com>
Tested-by: Daniel Barboza <danielhb(a)linux.vnet.ibm.com>
On 07/16/2014 06:52 PM, alinefm(a)linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm(a)linux.vnet.ibm.com>
For now, Kimchi just supports 2 types of user roles: 'admin' - user has
full control of Kimchi features and 'user' with limited access. But in
future the idea is to have more and more roles so it is good to
already provide the authorization support with that in mind.
That way, instead of only returning if user has or not sudo permissions, the
/login API will return the user role for each tab.
If the user has sudo permissions he/she will have 'admin' role,
otherwise, 'user' role.
curl -H "Content-Type: application/json" -H "Accept:
application/json"
http://localhost:8010/login
-d'{"username": "guest", "password":
"guest-passwd"}' -X POST
{
"username": "guest",
"roles": {"templates": "user",
"host": "user",
"storage": "user",
"guests": "user",
"network": "user"},
"groups": []
}
curl -H "Content-Type: application/json" -H "Accept:
application/json"
http://localhost:8010/login
-d'{"username": "sysadmin", "password":
"sysadmin-passwd"}' -X POST
{
"username": "sysadmin",
"roles": {"templates": "admin",
"host": "admin",
"storage": "admin",
"guests": "admin",
"network": "admin"},
"groups": []
}
Having one role per tab, give us more flexibility to assign different
roles to user.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/auth.py | 28 +++++++++++++++++++++++-----
src/kimchi/utils.py | 15 +++++++++++++++
tests/test_rest.py | 8 ++++++++
tests/utils.py | 6 +++---
4 files changed, 49 insertions(+), 8 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py
index 6a4a610..bf048b2 100644
--- a/src/kimchi/auth.py
+++ b/src/kimchi/auth.py
@@ -32,14 +32,17 @@
from kimchi import template
from kimchi.exception import InvalidOperation, OperationFailed
-from kimchi.utils import run_command
+from kimchi.utils import get_all_tabs, run_command
USER_NAME = 'username'
USER_GROUPS = 'groups'
USER_SUDO = 'sudo'
+USER_ROLES = 'roles'
REFRESH = 'robot-refresh'
+tabs = get_all_tabs()
+
def redirect_login():
url = "/login.html"
@@ -62,22 +65,36 @@ def __init__(self, username):
self.user = {}
self.user[USER_NAME] = username
self.user[USER_GROUPS] = None
- self.user[USER_SUDO] = False
+ # after adding support to change user roles that info should be read
+ # from a specific objstore and fallback to default only if any entry is
+ # found
+ self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
def get_groups(self):
self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall()
if self.user[USER_NAME] in g.gr_mem]
return self.user[USER_GROUPS]
+ def get_roles(self):
+ if self.has_sudo():
+ # after adding support to change user roles that info should be
+ # read from a specific objstore and fallback to default only if
+ # any entry is found
+ self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin')
+
+ return self.user[USER_ROLES]
+
def has_sudo(self):
result = multiprocessing.Value('i', 0, lock=False)
p = multiprocessing.Process(target=self._has_sudo, args=(result,))
p.start()
p.join()
- self.user[USER_SUDO] = bool(result.value)
- return self.user[USER_SUDO]
+
+ return result.value
def _has_sudo(self, result):
+ result.value = False
+
_master, slave = pty.openpty()
os.setsid()
fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
@@ -94,7 +111,7 @@ def _has_sudo(self, result):
self.user[USER_NAME]])
for line in out.split('\n'):
if line and re.search("(ALL)", line):
- result.value = 1
+ result.value = True
debug("User %s can run any command with sudo" %
result.value)
return
@@ -219,6 +236,7 @@ def login(username, password, **kwargs):
cherrypy.session[USER_NAME] = username
cherrypy.session[USER_GROUPS] = user.get_groups()
cherrypy.session[USER_SUDO] = user.has_sudo()
+ cherrypy.session[USER_ROLES] = user.get_roles()
cherrypy.session[REFRESH] = time.time()
cherrypy.session.release_lock()
return user.get_user()
diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py
index 97adbf8..a992189 100644
--- a/src/kimchi/utils.py
+++ b/src/kimchi/utils.py
@@ -27,6 +27,7 @@
import subprocess
import traceback
import urllib2
+import xml.etree.ElementTree as ET
from multiprocessing import Process, Queue
from threading import Timer
@@ -108,6 +109,20 @@ def get_enabled_plugins():
except (TypeError, KeyError):
continue
+def get_all_tabs():
+ files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')]
+
+ for plugin, _ in get_enabled_plugins():
+ files.append(os.path.join(PluginPaths(plugin).ui_dir,
+ 'config/tab-ext.xml'))
+
+ tabs = []
+ for f in files:
+ root = ET.parse(f)
+ tabs.extend([t.text.lower() for t in root.getiterator('title')])
+
+ return tabs
+
def import_class(class_path):
module_name, class_name = class_path.rsplit('.', 1)
diff --git a/tests/test_rest.py b/tests/test_rest.py
index 694d907..a96ccd4 100644
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -1552,6 +1552,14 @@ def test_auth_session(self):
req = json.dumps({'username': user, 'password': pw})
resp = self.request('/login', req, 'POST', hdrs)
self.assertEquals(200, resp.status)
+
+ user_info = json.loads(resp.read())
+ self.assertEquals(sorted(user_info.keys()),
+ ['groups', 'roles', 'username'])
+ roles = user_info['roles']
+ for tab, role in roles.iteritems():
+ self.assertEquals(role, u'admin')
+
cookie = resp.getheader('set-cookie')
hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py
index fd9b23c..4853b7a 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -157,8 +157,8 @@ def patch_auth(sudo=True):
def _get_groups(self):
return None
- def _has_sudo(self):
- return sudo
+ def _has_sudo(self, result):
+ result.value = sudo
def _authenticate(username, password, service="passwd"):
try:
@@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"):
import kimchi.auth
kimchi.auth.authenticate = _authenticate
kimchi.auth.User.get_groups = _get_groups
- kimchi.auth.User.has_sudo = _has_sudo
+ kimchi.auth.User._has_sudo = _has_sudo
def normalize_xml(xml_str):