
From: Leonardo Garcia <lagarcia@br.ibm.com> When the /login REST API is called with a valid username and password, Kimchi will find out the user groups and sudo status, store these values in sessions variables, and return them in a JSON message with the following format: {"sudo": true | false, "userid": "<username>", "groups": [<list of groups>]} For example: {"sudo": false, "userid": "guest", "groups": ["guest", "foo"]} This JSON return message can be used by the UI to figure out which tabs/options should be shown in the web interface according to the user rights. It is important to notice that sudo configuration for a given user is not as simple as an on/off key. Sudo permits various levels of access controls. For instance, it is possible to specify which executables a user can run with sudo. For the purpose of this implementation, we only consider a user to have sudo rights if this user can run any command in the system with sudo (which is equivalent to have root rights). Signed-off-by: Leonardo Garcia <lagarcia@br.ibm.com> --- src/kimchi/auth.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/kimchi/root.py | 4 ++-- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index f2c2a3e..e8deb40 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -24,15 +24,19 @@ import base64 import cherrypy +import grp import PAM import re from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed +from kimchi.utils import run_command -SESSION_USER = 'userid' +USER_ID = 'userid' +USER_GROUPS = 'groups' +USER_SUDO = 'sudo' def debug(msg): @@ -40,6 +44,46 @@ def debug(msg): # cherrypy.log.error(msg) +class User(object): + + def __init__(self, userid): + self.user = {} + self.user[USER_ID] = userid + self.user[USER_GROUPS] = None + self.user[USER_SUDO] = False + + def get_groups(self): + self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() + if self.user[USER_ID] in g.gr_mem] + return self.user[USER_GROUPS] + + def has_sudo(self): + out, err, exit = run_command(['sudo', '-l', '-U', self.user[USER_ID], + 'sudo']) + if exit == 0: + debug("User %s is allowed to run sudo" % self.user[USER_ID]) + # sudo allows a wide range of configurations, such as controlling + # which binaries the user can execute with sudo. + # For now, we will just check whether the user is allowed to run + # any command with sudo. + out, err, exit = run_command(['sudo', '-l', '-U', + self.user[USER_ID]]) + for line in out.split('\n'): + if line and re.search("(ALL)", line): + self.user[USER_SUDO] = True + debug("User %s can run any command with sudo" % + self.user[USER_ID]) + return self.user[USER_SUDO] + debug("User %s can only run some commands with sudo" % + self.user[USER_ID]) + else: + debug("User %s is not allowed to run sudo" % self.user[USER_ID]) + return self.user[USER_SUDO] + + def get_user(self): + return self.user + + def authenticate(username, password, service="passwd"): '''Returns True if authenticate is OK via PAM.''' def _pam_conv(auth, query_list, userData=None): @@ -88,12 +132,14 @@ def check_auth_session(): for the user. """ try: - user = cherrypy.session[SESSION_USER] - debug("Session authenticated for user %s" % user) + if cherrypy.session[USER_ID]: + debug("Session authenticated for user %s" % + cherrypy.session[USER_ID]) + return True except KeyError: - debug("Session not found") - return False - return True + pass + debug("Session not found") + return False def check_auth_httpba(): @@ -123,18 +169,21 @@ def check_auth_httpba(): def login(userid, password): if not authenticate(userid, password): debug("User cannot be verified with the supplied password") - return False + return None + user = User(userid) debug("User verified, establishing session") cherrypy.session.acquire_lock() cherrypy.session.regenerate() - cherrypy.session[SESSION_USER] = cherrypy.request.login = userid + cherrypy.session[USER_ID] = userid + cherrypy.session[USER_GROUPS] = user.get_groups() + cherrypy.session[USER_SUDO] = user.has_sudo() cherrypy.session.release_lock() - return True + return user.get_user() def logout(): cherrypy.session.acquire_lock() - cherrypy.session[SESSION_USER] = None + cherrypy.session[USER_ID] = None cherrypy.session.release_lock() cherrypy.lib.sessions.expire() diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 37d59e2..4a1a9cd 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -110,11 +110,11 @@ class KimchiRoot(Root): raise cherrypy.HTTPError(400, e.message) try: - auth.login(userid, password) + user_info = auth.login(userid, password) except OperationFailed: raise cherrypy.HTTPError(401) - return '{}' + return json.dumps(user_info) @cherrypy.expose def logout(self): -- 1.8.5.3