changeset 3004:0ae378dc1edf

updated ldap support from 1.5 branch: tls support, configuration defaults, comments/docs (port from 1.6)
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 06 Jan 2008 18:48:58 +0100
parents f54c41b3b7ce
children ea3e77c4ab52
files MoinMoin/auth/ldap_login.py MoinMoin/config/multiconfig.py
diffstat 2 files changed, 59 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/auth/ldap_login.py	Sun Jan 06 18:31:47 2008 +0100
+++ b/MoinMoin/auth/ldap_login.py	Sun Jan 06 18:48:58 2008 +0100
@@ -5,11 +5,18 @@
     This code only creates a user object, the session has to be established by
     the auth.moin_session auth plugin.
 
+    python-ldap needs to be at least 2.0.0pre06 (available since mid 2002) for
+    ldaps support - some older debian installations (woody and older?) require
+    libldap2-tls and python2.x-ldap-tls, otherwise you get ldap.SERVER_DOWN:
+    "Can't contact LDAP server" - more recent debian installations have tls
+    support in libldap2 (see dependency on gnutls) and also in python-ldap.
+
     TODO: migrate configuration items to constructor parameters,
           allow more configuration (alias name, ...) by using
           callables as parameters
 
-    @copyright: 2006 MoinMoin:ThomasWaldmann, Nick Phillips
+    @copyright: 2006-2007 MoinMoin:ThomasWaldmann,
+                2006 Nick Phillips
     @license: GNU GPL, see COPYING for details.
 """
 import sys
@@ -18,11 +25,11 @@
 from MoinMoin import user
 from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
 
+
 class LDAPAuth(BaseAuth):
-    """ get authentication data from form, authenticate against LDAP (or Active Directory),
-        fetch some user infos from LDAP and create a user profile for that user that must
-        be used by subsequent auth plugins (like moin_cookie) as we never return a user
-        object from ldap_login.
+    """ get authentication data from form, authenticate against LDAP (or Active
+        Directory), fetch some user infos from LDAP and create a user object
+        for that user. The session is kept by the moin_session auth plugin.
     """
 
     login_inputs = ['username', 'password']
@@ -48,23 +55,38 @@
                 dn = None
                 coding = cfg.ldap_coding
                 if verbose: request.log("LDAP: Setting misc. options...")
-                # needed for Active Directory:
-                ldap.set_option(ldap.OPT_REFERRALS, 0)
+                ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
+                ldap.set_option(ldap.OPT_REFERRALS, cfg.ldap_referrals)
+                ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, cfg.ldap_timeout)
+
+                starttls = cfg.ldap_start_tls
+                if ldap.TLS_AVAIL:
+                    for option, value in (
+                        (ldap.OPT_X_TLS_CACERTDIR, cfg.ldap_tls_cacertdir),
+                        (ldap.OPT_X_TLS_CACERTFILE, cfg.ldap_tls_cacertfile),
+                        (ldap.OPT_X_TLS_CERTFILE, cfg.ldap_tls_certfile),
+                        (ldap.OPT_X_TLS_KEYFILE, cfg.ldap_tls_keyfile),
+                        (ldap.OPT_X_TLS_REQUIRE_CERT, cfg.ldap_tls_require_cert),
+                        (ldap.OPT_X_TLS, starttls),
+                        #(ldap.OPT_X_TLS_ALLOW, 1),
+                    ):
+                        if value:
+                            ldap.set_option(option, value)
 
                 server = cfg.ldap_uri
-                if server.startswith('ldaps:'):
-                    # TODO: refactor into LDAPAuth() constructor arguments!
-                    # this is needed for self-signed ssl certs:
-                    ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
-                    # more stuff to try:
-                    #ldap.set_option(ldap.OPT_X_TLS_ALLOW, 1)
-                    #ldap.set_option(ldap.OPT_X_TLS_CERTFILE, LDAP_CACERTFILE)
-                    #ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,'/etc/httpd/ssl.crt/myCA-cacerts.pem')
-
                 if verbose: request.log("LDAP: Trying to initialize %r." % server)
                 l = ldap.initialize(server)
                 if verbose: request.log("LDAP: Connected to LDAP server %r." % server)
 
+                if starttls and server.startswith('ldap:'):
+                    if verbose: request.log("LDAP: Trying to start TLS to %r." % server)
+                    try:
+                        l.start_tls_s()
+                        if verbose: request.log("LDAP: Using TLS to %r." % server)
+                    except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR), err:
+                        if verbose: request.log("LDAP: Couldn't establish TLS to %r (err: %s)." % (server, str(err)))
+                        raise
+  
                 # you can use %(username)s and %(password)s here to get the stuff entered in the form:
                 ldap_binddn = cfg.ldap_binddn % locals()
                 ldap_bindpw = cfg.ldap_bindpw % locals()
@@ -115,6 +137,8 @@
                 try:
                     aliasname = ldap_dict[cfg.ldap_aliasname_attribute][0]
                 except (KeyError, IndexError):
+                    pass
+                if not aliasname:
                     sn = ldap_dict.get(cfg.ldap_surname_attribute, [''])[0]
                     gn = ldap_dict.get(cfg.ldap_givenname_attribute, [''])[0]
                     if sn and gn:
@@ -124,10 +148,10 @@
                 aliasname = aliasname.decode(coding)
 
                 if email:
-                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author',))
+                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'email', 'mailto_author', ))
                     u.email = email
                 else:
-                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author',))
+                    u = user.User(request, auth_username=username, password="{SHA}NotStored", auth_method=self.name, auth_attribs=('name', 'password', 'mailto_author', ))
                 u.name = username
                 u.aliasname = aliasname
                 u.remember_me = 0 # 0 enforces cookie_lifetime config param
--- a/MoinMoin/config/multiconfig.py	Sun Jan 06 18:31:47 2008 +0100
+++ b/MoinMoin/config/multiconfig.py	Sun Jan 06 18:48:58 2008 +0100
@@ -330,7 +330,10 @@
     language_ignore_browser = False # ignore browser settings, use language_default
                                     # or user prefs
 
-    # ldap / active directory server URI:
+    # ldap / active directory server URI
+    # use ldaps://server:636 url for ldaps,
+    # use  ldap://server for ldap without tls (and set ldap_start_tls to 0),
+    # use  ldap://server for ldap with tls (and set ldap_start_tls to 1 or 2).
     ldap_uri = 'ldap://localhost'
 
     # We can either use some fixed user and password for binding to LDAP.
@@ -342,6 +345,8 @@
     # or we can use the username and password we got from the user:
     #ldap_binddn = '%(username)s@example.org' # DN we use for first bind (AD)
     #ldap_bindpw = '%(password)s' # password we use for first bind
+    # or we can bind anonymously (if that is supported by your directory).
+    # In any case, ldap_binddn and ldap_bindpw must be defined.
     ldap_binddn = ''
     ldap_bindpw = ''
 
@@ -352,6 +357,9 @@
     # scope of the search we do (2 == ldap.SCOPE_SUBTREE)
     ldap_scope = 2 # we do not want to import ldap for everybody just for that
 
+    # LDAP REFERRALS
+    ldap_referrals = 0 # (0 needed for AD)
+
     # ldap filter used for searching:
     #ldap_filter = '(sAMAccountName=%(username)s)' # (AD)
     ldap_filter = '(uid=%(username)s)' # (OpenLDAP)
@@ -369,6 +377,14 @@
     ldap_timeout = 10 # how long we wait for the ldap server [s]
     ldap_verbose = True # if True, put lots of LDAP debug info into the log
 
+    # TLS / SSL related defaults
+    ldap_start_tls = 0 # 0 = No, 1 = Try, 2 = Required
+    ldap_tls_cacertdir = ''
+    ldap_tls_cacertfile = ''
+    ldap_tls_certfile = ''
+    ldap_tls_keyfile = ''
+    ldap_tls_require_cert = 0 # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
+
     log_reverse_dns_lookups = True  # if we do reverse dns lookups for logging hostnames
                                     # instead of just IPs
     log_timing = False              # update <data_dir>/timing.log?