[Kimchi-devel] [PATCH 3/5] Find out user groups and sudo status during login.

Leonardo Garcia lagarcia at linux.vnet.ibm.com
Mon Feb 10 02:32:32 UTC 2014


From: Leonardo Garcia <lagarcia at 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 at br.ibm.com>
---
 src/kimchi/auth.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/kimchi/root.py |  4 ++--
 2 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py
index f9873ca..3ffe4b1 100644
--- a/src/kimchi/auth.py
+++ b/src/kimchi/auth.py
@@ -30,9 +30,12 @@ import re
 
 from kimchi import template
 from kimchi.exception import OperationFailed
+from kimchi.utils import run_command, parse_cmd_output
 
 
-SESSION_USER = 'userid'
+USER_ID = 'userid'
+USER_GROUPS = 'groups'
+USER_SUDO = 'sudo'
 
 
 def debug(msg):
@@ -40,6 +43,52 @@ 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] = []
+        self.user[USER_SUDO] = False
+
+    def get_groups(self):
+        out, err, exit = run_command(['groups', self.user[USER_ID]])
+        # the output of the groups command is of the form:
+        # <userid> : <list of groups separated by space>
+        if exit == 0:
+            out = parse_cmd_output(out, ['userid', 'groups'], ':')
+            self.user[USER_GROUPS] = out[0]['groups'].split()
+            debug("Groups for %s: %s" % (self.user[USER_ID],
+                                         self.user[USER_GROUPS]))
+        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):
@@ -122,13 +171,16 @@ 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():
diff --git a/src/kimchi/root.py b/src/kimchi/root.py
index ce4a49c..8e90b7f 100644
--- a/src/kimchi/root.py
+++ b/src/kimchi/root.py
@@ -106,11 +106,11 @@ class KimchiRoot(Root):
             raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % key)
 
         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




More information about the Kimchi-devel mailing list