As far as I know, MoinMoin does not have support for PAM authentication. I use MoinMoin 1.5.2 (last package for ubuntu sarge). I needed to join current authentication system and not to create a new one. So I copied MoinMoin.auth.moin_cookie method to the new file (pam_auth.py) and modified it. My version of the method is called pam_cookie.

pam_auth.py

# -*- coding: iso-8859-1 -*-
"""
    @copyright: 2007 by Pavel Hančar <hancar@email.cz>
    @license: GNU GPL, see COPYING for details.
"""

import PAM

import Cookie
from MoinMoin import user


class PAMInternalError(Exception):
    """ Internal error in PAM authentication """

def pam_cookie(request, **kw):

    def pam_cookie_conv(auth, query_list, userData):
        resp = []
        for i in range(len(query_list)):
            query, type = query_list[i]
            if type == PAM.PAM_PROMPT_ECHO_ON:
                val = kw.get('name')
                resp.append((val, 0))
            elif type == PAM.PAM_PROMPT_ECHO_OFF:
                val =kw.get('password')
                resp.append((val, 0))
            elif type == PAM.PAM_PROMPT_ERROR_MSG or type == PAM.PAM_PROMPT_TEXT_INFO:
                resp.append(('', 0))
            else:
                return None
        return resp

    # authenticate via the MOIN_ID cookie
    if kw.get('login'):
        name = kw.get('name')
        password = kw.get('password')

        auth_pam = PAM.pam()
        auth_pam.start('passwd')
        auth_pam.set_item(PAM.PAM_USER, name)
        auth_pam.set_item(PAM.PAM_CONV, pam_cookie_conv)
        try:
            auth_pam.authenticate()
            auth_pam.acct_mgmt()
        except PAM.error, resp:
            pass
        except:
            raise PAMInternalError('PAM internal error\n')
        else:
            u = user.User(request, name=name, password=password,
                          auth_method='login_userpassword')
            if u.valid:
                request.user = u # needed by setCookie
                request.setCookie()
                return u, False
          return None, True

    if kw.get('logout'):
        # clear the cookie in the browser and locally. Does not
        # check if we have a valid user logged, just make sure we
        # don't have one after this call.
        request.deleteCookie()
        return None, True
    try:
        cookie = Cookie.SimpleCookie(request.saved_cookie)
    except Cookie.CookieError:
        # ignore invalid cookies, else user can't relogin
        cookie = None
    if cookie and cookie.has_key('MOIN_ID'):
        u = user.User(request, id=cookie['MOIN_ID'].value,
                      auth_method='pam_cookie', auth_attribs=())
        if u.valid:
            return u, False
    return None, True              

Then I inserted two lines to wikiconfig.py:

from pam_auth import pam_cookie
.
.
auth=[pam_cookie]

Now it works -- only those users, which have an account in PAM, can create their MoinMoin account and login. But this mechanism is not ideal. The creating of MoinMoin accounts is possible only by useless writing of two passwords and e-mail. Maybe I will copy and modify macro UserPreferences. It means to reimplement some method(s) in userform.


I am sorry, I didn't test it properly. The pam_auth above has a defect. There is possible to create an account, which one can not login because of PAM.

This is better:

pam_auth.py

# -*- coding: iso-8859-1 -*-
"""
@copyright: 2007 by Pavel Hančar <hancar@email.cz>
@license: GNU GPL, see COPYING for details.
"""
import PAM,Cookie
from MoinMoin import user,wikiutil,i18n


class PAMInternalError(Exception):
    """ Internal error in PAM authentication """

def pam_cookie(request, **kw):
    def pam_cookie_conv(auth, query_list, userData):
        resp = []
        for i in range(len(query_list)):
            query, type = query_list[i]
            if type == PAM.PAM_PROMPT_ECHO_ON:
                val = kw.get('name')
                resp.append((val, 0))
            elif type == PAM.PAM_PROMPT_ECHO_OFF:
                val =kw.get('password')
                resp.append((val, 0))
            elif type == PAM.PAM_PROMPT_ERROR_MSG or type == PAM.PAM_PROMPT_TEXT_INFO:
                resp.append(('', 0))
            else:
                return None
        return resp

    # authenticate via the MOIN_ID cookie
    f=open("pamlog","w");
    f.write(str(kw)+"\n")
    f.close
    # login or create account
    if kw.get('login') or request.form.has_key("create"):
        name = kw.get('name')
        password = kw.get('password')

        auth_pam = PAM.pam()
        auth_pam.start('passwd')
        auth_pam.set_item(PAM.PAM_USER, name)
        auth_pam.set_item(PAM.PAM_CONV, pam_cookie_conv)
        try:
            auth_pam.authenticate()
            auth_pam.acct_mgmt()
        except PAM.error, resp:
            request.deleteCookie()
            if "getText" in dir(request):
                _=request.getText
                msg=_("PAM error: invalid user name or password")
                page = wikiutil.getSysPage(request, 'UserPreferences')
                page.send_page(request, msg=msg)
        except:
            raise PAMInternalError('PAM internal error\n')
        else:
            if kw.get('login'):
                u = user.User(request, name=name, password=password,
                              auth_method='login_userpassword')
                if u.valid:
                    request.user = u # needed by setCookie
                    request.setCookie()
                    return u, False
            return None, True

    if kw.get('logout'):
        # clear the cookie in the browser and locally. Does not
        # check if we have a valid user logged, just make sure we
        # don't have one after this call.
        request.deleteCookie()
        return None, True
    try:
        cookie = Cookie.SimpleCookie(request.saved_cookie)
    except Cookie.CookieError:
        # ignore invalid cookies, else user can't relogin
        cookie = None
    if cookie and cookie.has_key('MOIN_ID'):
        u = user.User(request, id=cookie['MOIN_ID'].value,
                      auth_method='pam_cookie', auth_attribs=())
        if u.valid:
            return u, False
    return None, True

These lines are needed in wikiconfig.py

from pam_auth import pam_cookie
.
.
auth=[pam_cookie]

and good is also

user_form_remove=["password2"]
user_form_disable=["name","password"]

because it's not configurable, it's in PAM.

But if you don't want repeat password while creating account (which is useless because it's proved by PAM), you need new implementation of action userform. So put the file userform.py into directory data/plugin/action/.

userform.py

# -*- coding: iso-8859-1 -*-
"""
    @copyright: 2007 by Pavel Hančar <hancar@email.cz>
    @license: GNU GPL, see COPYING for details.
"""

from MoinMoin import userform
from MoinMoin.Page import Page
def execute(pagename,request):
    form=request.form
    if form.has_key("create"):
        form["password2"]=form["password"]
    savemsg = userform.savedata(request)
    Page(request, pagename).send_page(request, msg=savemsg)

What about the copyright? Maybe Jürgen Hermann is more the author of this action than me, because in wikiaction.py is this:

    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>
    @license: GNU GPL, see COPYING for details.

(!) If you reused code from Juergen, but added own code, too, then just add yourself to the (C) line.

(!) How about doing this for 1.6?


We wanted PAM-based auth for our hackerspace's internal wiki so I've had another go at this. I'm attaching a patch against the 1.9 branch for a pam_login auth method. moin-1.9-pamauth.patch If accepted, I'm happy to write up a Howto on the wiki here as well.

It is correct that if you're using pam_unix with a shadow password, moin needs to run with elevated privileges. However it doesn't need to run as root, you can run as a group with read-only access to the shadow file, as documented by mod_auth_pam authors here: http://pam.sourceforge.net/mod_auth_pam/shadow.html Some distros (Debian-based, maybe others?) come preconfigured with a 'shadow' group for this purpose. As the mod_auth_pam author says, you are weakening your system security, albeit only a little.

You also don't need any elevated privileges at all if you're using PAM with a different backend (LDAP, windows, etc.)

Cheers (moinmoin is a great wiki btw, very happy to have such a quality Python-based option!) -- -- AngusGratton 2012-08-05 09:49:28


CategoryFeatureRequest

MoinMoin: FeatureRequests/AuthenticationWithPAM (last edited 2012-08-05 09:51:08 by AngusGratton)