diff -r a8a777074233 MoinMoin/auth/ldap_login.py
--- a/MoinMoin/auth/ldap_login.py	Mon Jul 27 01:55:59 2009 +0200
+++ b/MoinMoin/auth/ldap_login.py	Wed Jul 29 14:53:47 2009 +0200
@@ -229,6 +229,7 @@
                 else:
                     u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ))
                 u.name = username
+                u.dn = dn
                 u.aliasname = aliasname
                 u.remember_me = 0 # 0 enforces cookie_lifetime config param
                 logging.debug("creating userprefs with name %r email %r alias %r" % (username, email, aliasname))
diff -r a8a777074233 MoinMoin/datastruct/backends/ldap_groups.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/datastruct/backends/ldap_groups.py	Wed Jul 29 14:53:47 2009 +0200
@@ -0,0 +1,87 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - ldap group lazy backend.
+
+    The ldap group backend allows one to define groups in an
+    ldap server. Group members can must be authenticated via
+    ldap. 
+
+    @copyright: 2009 Benoit Peccatte(peck)
+    @license: GPL, see COPYING for details
+"""
+
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
+from MoinMoin.datastruct.backends import LazyGroup, LazyGroupsBackend
+from MoinMoin.util.ldap_connection import LDAPConnection
+import ldap
+from MoinMoin.user import User
+
+class LdapGroup(LazyGroup):
+    pass
+
+
+class LdapGroups(LazyGroupsBackend):
+
+    def __init__(self, request, 
+        ldap_connection,           # LDAPConnection to use for connections
+        base_dn='',                # base ldap dn to use when searching for groups
+        scope=ldap.SCOPE_SUBTREE,  # scope of the search 
+        filter='',                 # filter to use when searching for groups 
+        member_attribute='member', # attribute name for group members
+        name_attribute='cn',       # attribute name for group name
+        ):
+        
+        super(LdapGroups, self).__init__(request)
+        self.base = base_dn
+        self.scope = scope
+        if(filter != '' and filter[0] != '('):
+            filter = '(' + filter + ')'
+        self.filter = filter
+        self.member_attribute = member_attribute
+        self.name_attribute = name_attribute
+        self.ldap = ldap_connection
+
+    def __contains__(self, group_name):
+        filter = self._getfilter(group_name)
+        res = self.ldap.search_single(self.base, self.scope, filter, 'dn')
+        return res != []
+
+    def __iter__(self):
+        filter = self._getfilter('*')
+        return self.ldap.search_single(self.base, self.scope, filter, 'dn')
+
+    def __getitem__(self, group_name):
+        return LdapGroup(self.request, group_name, self)
+
+    def _iter_group_members(self, group_name):
+        filter = self._getfilter(group_name)
+        return self.ldap.search_single( self.base, self.scope, filter, self.member_attribute)
+
+    def _group_has_member(self, group_name, member):
+        # only check users that are authenticated using ldap
+        u = User(self.request, auth_username=member)
+        if not hasattr(u,'dn'):
+            return False
+        filter = self._getfilter(group_name, u.dn)
+        res = self.ldap.search_single( self.base, self.scope, filter, 'dn')
+        return res != []
+
+    def _getfilter(self, group_name, member=None):
+        """ Create a filter string for a group. 
+            Add a member filter if one is provided."""
+        # extract real group name from the form XxxGroup
+        grp = self.page_group_regex.search(group_name)
+        if(grp == None):
+            return False
+        group = grp.group(2)
+        
+        filter = '(' + self.name_attribute + '=' + group + ')'
+        if(self.filter != ''):
+            filter = '(&' + self.filter + filter + ')'
+        
+        if member == None:
+            return filter
+        return '(&' + filter + '(' + self.member_attribute + '=' + member + '))'
+
diff -r a8a777074233 MoinMoin/util/ldap_connection.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/ldap_connection.py	Wed Jul 29 14:53:47 2009 +0200
@@ -0,0 +1,133 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - ldap connection object
+
+    The connexion object holds informations to connect to a single 
+    ldap server.
+
+    @copyright: 2009 Benoit Peccatte(peck)
+    @license: GPL, see COPYING for details
+"""
+
+from MoinMoin import log
+logging = log.getLogger(__name__)
+
+try:
+    import ldap
+except ImportError, err:
+    logging.error("You need to have python-ldap installed (%s)." % str(err))
+    raise
+
+class LDAPConnection:
+    def __init__(self, 
+        server_uri='ldap://localhost',  # ldap / active directory server URI
+                                        # use ldaps://server:636 url for ldaps,
+                                        # use  ldap://server for ldap without tls (and set start_tls to 0),
+                                        # use  ldap://server for ldap with tls (and set start_tls to 1 or 2).
+        bind_dn='',  # We can only use fixed user and password here
+                     # TODO implement "the username and password we got from the user" in ldap_auth
+        bind_pw='',
+        referrals=0, # LDAP REFERRALS (0 needed for AD)
+        coding='utf-8', # coding used for ldap queries and result values
+        timeout=10, # how long we wait for the ldap server [s]
+        start_tls=0, # 0 = No, 1 = Try, 2 = Required
+        tls_cacertdir=None,
+        tls_cacertfile=None,
+        tls_certfile=None,
+        tls_keyfile=None,
+        tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
+        ):
+
+        self.server_uri = server_uri
+        self.bind_dn = bind_dn
+        self.bind_pw = bind_pw
+        self.referrals = referrals
+        self.coding = coding
+        self.timeout = timeout
+        self.start_tls = start_tls
+        self.tls_cacertdir = tls_cacertdir
+        self.tls_cacertfile = tls_cacertfile
+        self.tls_certfile = tls_certfile
+        self.tls_keyfile = tls_keyfile
+        self.tls_require_cert = tls_require_cert
+
+        self.ldap = None
+
+    def __connect(self):
+        """ Make a new connection to ldap only if needed.
+            Public method should call this."""
+        if (self.ldap != None):
+            return 
+        self.ldap = self.__new_ldap(self.bind_dn, self.bind_pw)
+
+    def __new_ldap(self, binddn, password):
+        """ Create a connection object and bind. """
+        try:
+            # options
+            logging.debug("Setting misc. ldap options...")
+            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)
+            ldap.set_option(ldap.OPT_REFERRALS, self.referrals)
+            ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
+            if hasattr(ldap, 'TLS_AVAIL') and ldap.TLS_AVAIL:
+                for option, value in (
+                    (ldap.OPT_X_TLS_CACERTDIR, self.tls_cacertdir),
+                    (ldap.OPT_X_TLS_CACERTFILE, self.tls_cacertfile),
+                    (ldap.OPT_X_TLS_CERTFILE, self.tls_certfile),
+                    (ldap.OPT_X_TLS_KEYFILE, self.tls_keyfile),
+                    (ldap.OPT_X_TLS_REQUIRE_CERT, self.tls_require_cert),
+                    (ldap.OPT_X_TLS, self.start_tls),
+                    #(ldap.OPT_X_TLS_ALLOW, 1),
+                ):
+                    if value is not None:
+                        ldap.set_option(option, value)
+
+            # connect and bind
+            logging.debug("Trying to initialize %r." % self.server_uri)
+            l = ldap.initialize(self.server_uri)
+            l.protocol_version = ldap.VERSION3 # recommended by documentation
+            logging.debug("Connected to LDAP server %r." % self.server_uri)
+            if self.start_tls and server.startswith('ldap:'):
+                logging.debug("Trying to start TLS to %r." % self.server_uri)
+                try:
+                    l.start_tls_s()
+                    logging.debug("Using TLS to %r." % self.server_uri)
+                except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR), err:
+                    logging.warning("Couldn't establish TLS to %r (err: %s)." % (self.server_uri, str(err)))
+                    raise
+
+            l.simple_bind_s(binddn.encode(self.coding), password.encode(self.coding))
+            logging.debug("Bound with binddn %r" % binddn)
+            return l
+        except ldap.LDAPError, error_message:
+            print "LDAP error %r" % error_message
+            raise
+
+    def authenticate(self, binddn, passwd):
+        # TODO for ldap_auth.py
+        return
+
+    def search(self, base, scope, filter, attributes):
+        """ Make a specific search on the ldap server """
+        self.__connect() 
+        try:
+            result_id = self.ldap.search(base.encode(self.coding), 
+                                         scope, 
+                                         filter.encode(self.coding),
+                                         attributes)
+            result_type, result_data = self.ldap.result(result_id, self.timeout)
+            if result_type == ldap.RES_SEARCH_RESULT:
+                return result_data
+            else:
+                print "unknown result_type"
+        except ldap.LDAPError, error_message:
+            print "search error %s" % error_message
+
+    def search_single(self, base, scope, filter, attribute):
+        """ Search for a single attribute, returns an array of single values. """
+        res = self.search(base, scope, filter, [attribute])
+        if(attribute == 'dn'):
+            first = lambda x: x[0]
+        else:
+            first = lambda x: x[1][attribute][0]
+        return map(first, res)
+
