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

Daniel H Barboza danielhb at linux.vnet.ibm.com
Mon Feb 10 12:09:58 UTC 2014


Reviewed-by: Daniel Barboza <danielhb at linux.vnet.ibm.com>

On 02/10/2014 12:32 AM, Leonardo Garcia wrote:
> 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):




More information about the Kimchi-devel mailing list