[Kimchi-devel] [PATCH v2 2/4] Find out user groups and sudo status during login.

Leonardo Garcia lagarcia at linux.vnet.ibm.com
Thu Feb 13 02:28:17 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 | 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




More information about the Kimchi-devel mailing list