Log note : check LDAP group for membership and more
changed:
-
-
**From: Denis Shaposhnikov**

**Date: Sat, 5 Jan 2008 21:37:56 +0300**

I want to share my expirience with configuring Roundup to use LDAP for authentication. I've found that the LDAPLogin (see above) is not exactly what I need. It creates new Roundup user after successfull LDAP authentication but use empty password. And it checks local password first. So, after first LDAP authentication anyone can login with empty password.

OK, I've done some modifications and you can find 'ldaplogin.py' below. Just configure it and put to 'extenstions' directory. This module can do local authentication too but it disabled by default, see above for the reason. My module do the next steps for authentication:

1. Try to bind with user's DN (without search).

2. Check that user is a member of configured group.

   This check is optional.

3. If no Roundup's user in the database, create it.

   It creates a user with password and with some fields from LDAP.

Here the 'ldaplogin.py' module::

    """Authentication an user via LDAP."""

    import ldap
    from roundup import password as PW
    from roundup.cgi import exceptions
    from roundup.cgi.actions import LoginAction
    from roundup.i18n import _

    class LDAPLoginAction(LoginAction):
        """Do LDAP authentication.

        Do LDAP (and may be local) authentication and create new roundup
        user if it is not exists. If property `ldap_req_group` have any
        value the user should be a memeber of that LDAP group.

        Set `use_local_auth` if you want to use local authentication and
        LDAP next. If not, it authenticates by LDAP only.
        """

        ldap_attrs = (
            ('address', 'mail'),
            ('organisation', 'o'),
            ('realname', 'cn'),
            ('phone', 'telephoneNumber'),
            ('username', 'uid'))
        ldap_dn = 'uid=%s,ou=people,dc=example,dc=com'
        ldap_group_attr = 'memberUid'
        ldap_req_group = 'cn=Roundup Users,ou=groups,dc=example,dc=com'
        ldap_server = 'ldap://127.0.0.1/'

        use_local_auth = False              # LDAP only

        def local_login (self, password):
            """Try local authentication."""
            # make sure the user exists
            try:
                self.client.userid = self.db.user.lookup(self.client.user)
            except KeyError:
                self.client.error_message.append(
                    _('Unknown user "%s"') % self.client.user)
                return 0

            # verify the password
            if not self.verifyPassword(self.client.userid, password):
                self.client.error_message.append(_('Invalid password'))
                return 0

            # Determine whether the user has permission to log in.
            # Base behaviour is to check the user has "Web Access".
            if not self.hasPermission("Web Access"):
                raise exceptions.LoginError, self._(
                    "You do not have permission to login")

            return 1

        def ldap_login(self, password):
            """Try LDAP authentication."""
            ldapconn = ldap.initialize(self.ldap_server)
            # verify the password
            try:
                ldapconn.bind_s(self.ldap_dn % self.client.user, password)
            except:
                self.client.error_message.append (_('Invalid password'))
                return 0

            if self.ldap_req_group:
                # verify the group membership
                member = None
                try:
                    member = ldapconn.compare_s(
                        self.ldap_req_group, self.ldap_group_attr, self.client.user)
                except:
                    member = None
                if not member:
                    self.client.error_message.append (
                        _("You do not have permission to login"))
                    return 0

            return 1

        def verifyLogin(self, username, password):
            # try to login throught LDAP or with local account
            ldap_ok = None
            if self.use_local_auth:
                if not self.local_login(password):
                    ldap_ok = self.ldap_login(password)
                    if not ldap_ok:
                        raise exceptions.LoginError
            else:
                ldap_ok = self.ldap_login(password)
                if not ldap_ok:
                    raise exceptions.LoginError
            self.client.error_message = []
            # reload user profile, or create it automatically if missing
            try:
                self.client.userid = self.db.user.lookup(self.client.user)
            except:
                if ldap_ok:
                    ldapconn = ldap.initialize(self.ldap_server)
                    ldaps = ldapconn.search_s(
                        self.ldap_dn % self.client.user, ldap.SCOPE_BASE)
                    attrs = ldaps[0][1]
                    props = {}
                    for user_attr,ldap_attr in self.ldap_attrs:
                        props[user_attr] = attrs.get(ldap_attr,('',))[0]
                    props['password'] = PW.Password(password)
                    self.journaltag = 'admin'
                    cl = self.db.user
                    props['roles'] = self.db.config.NEW_WEB_USER_ROLES
                    self.userid = cl.create(**props)
                    self.db.commit()
                    self.client.userid = self.db.user.lookup(self.client.user)
                else:
                    raise exceptions.LoginError, _(
                        "No account created without LDAP account")

    def init(instance):
        instance.registerAction('login', LDAPLoginAction)