I had some fun today setting up trac with apache and mod-auth-pam, and
I wondered if the same would be possible using paste.

I wrote this module to try and get PAM authentication working in
paste.  It depends on the PAM module, and also the pwd and grp
modules, so it's pretty much unix specific.

I have it located in paste.auth.pam.

I haven't gotten around to testing it with trac, but the example at
the bottom of the module works well for me.  You have to run it as
root if you're using shadow passwords so the PAM subsystem can read
the shadow file.

Thoughts, comments, criticisms or suggestions are welcome!

Cheers,
Chris
# (c) 2006 Chris AtLee <[EMAIL PROTECTED]>
# This module is part of the Python Paste Project and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
"""
PAM Authentication

This module provides a PamAuth class which can be used
by any of the paste auth modules to perform user authentication using
PAM.

Requires the PAM python module.

Example usage:
>>> from paste.wsgilib import dump_environ
>>> from paste.httpserver import serve
>>> from paste.auth.basic import AuthBasicHandler
>>> realm = 'Test Realm'
>>> auth = PamAuth("passwd")
>>> serve(AuthBasicHandler(dump_environ, "Test realm", auth))
"""
import PAM
import grp, pwd, sets
__all__ = ['PamAuth']

def _groups(user):
    """
    Returns a set of all groups the given user is a member of
    Raises KeyError if the user is not found in the group database
    """
    retval = []
    gid = pwd.getpwnam(user).pw_gid
    for g in grp.getgrall():
        if user in g.gr_mem or gid == g.gr_gid:
            retval.append(g.gr_name)
    return sets.ImmutableSet(retval)

class _PamConv:
    """
    Helper class to respond to password prompt from PAM
    """
    def __init__(self, password):
        self.password = password

    def __call__(self, auth, queryList, userData):
        resp = []
        for query, typ in queryList:
            if typ == PAM.PAM_PROMPT_ECHO_OFF:
                resp.append((self.password, 0))
        return resp

class PamAuth:
    """
    PAM Authentication class

    Parameters:
        
        ``service``

            Which PAM service to authenticate against.  Examples could be
            ``passwd``, ``login``, ``httpd``

        ``groups``

            A list of groups the user must be in one of to be authenticated.
            Set to None (the default) if a user can be in any group.
    """

    def __init__(self, service="passwd", groups=None):
        self.groups = groups
        self.auth = PAM.pam()
        self.auth.start(service)

    def __call__(self, environ, username, password):
        self.auth.set_item(PAM.PAM_USER, username)
        self.auth.set_item(PAM.PAM_CONV, _PamConv(password))
        try:
            self.auth.authenticate()
            self.auth.acct_mgmt()
        except PAM.error, resp:
            # Try and forget the password (the previous _PamConv had
            # a reference to the password string; by replacing it with
            # a new instance, the old one should be garbage collected)
            self.auth.set_item(PAM.PAM_CONV, _PamConv(""))
            return False
        else:
            # Try and forget the password
            self.auth.set_item(PAM.PAM_CONV, _PamConv(""))
            # Check group membership
            if self.groups is not None:
                if _groups(username).intersection(self.groups):
                    return True
                else:
                    return False
            return True

if "__main__" == __name__:
    from paste.wsgilib import dump_environ
    from paste.httpserver import serve
    from paste.auth.basic import AuthBasicHandler
    realm = 'Test Realm'
    auth = PamAuth("passwd", ["users",])
    serve(AuthBasicHandler(dump_environ, "Test realm", auth))
_______________________________________________
Paste-users mailing list
[email protected]
http://webwareforpython.org/cgi-bin/mailman/listinfo/paste-users

Reply via email to