changeset 3476:e818988b5b91

refactored ldap auth, move configuration values to auth object initialization
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 05 Apr 2008 16:39:50 +0200
parents 59a4005672d3
children b5785334d181
files MoinMoin/auth/ldap_login.py MoinMoin/config/multiconfig.py
diffstat 2 files changed, 108 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/auth/ldap_login.py	Sat Apr 05 11:48:47 2008 +0200
+++ b/MoinMoin/auth/ldap_login.py	Sat Apr 05 16:39:50 2008 +0200
@@ -2,8 +2,8 @@
 """
     MoinMoin - LDAP / Active Directory authentication
 
-    This code only creates a user object, the session has to be established by
-    the auth.moin_session auth plugin.
+    This code only creates a user object, the session will be established by
+    moin automatically.
 
     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
@@ -11,9 +11,7 @@
     "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
+    TODO: allow more configuration (alias name, ...) by using callables as parameters
 
     @copyright: 2006-2008 MoinMoin:ThomasWaldmann,
                 2006 Nick Phillips
@@ -31,20 +29,90 @@
 class LDAPAuth(BaseAuth):
     """ 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.
+        for that user. The session is kept by moin automatically.
     """
 
     login_inputs = ['username', 'password']
     logout_possible = True
     name = 'ldap'
 
+    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 either use some fixed user and password for binding to LDAP.
+                     # Be careful if you need a % char in those strings - as they are used as
+                     # a format string, you have to write %% to get a single % in the end.
+                     #bind_dn = 'binduser@example.org' # (AD)
+                     #bind_dn = 'cn=admin,dc=example,dc=org' # (OpenLDAP)
+                     #bind_pw = 'secret'
+                     # or we can use the username and password we got from the user:
+                     #bind_dn = '%(username)s@example.org' # DN we use for first bind (AD)
+                     #bind_pw = '%(password)s' # password we use for first bind
+                     # or we can bind anonymously (if that is supported by your directory).
+                     # In any case, bind_dn and bind_pw must be defined.
+        bind_pw='',
+        base_dn='',  # base DN we use for searching
+                     #base_dn = 'ou=SOMEUNIT,dc=example,dc=org'
+        scope=ldap.SCOPE_SUBTREE, # scope of the search we do (2 == ldap.SCOPE_SUBTREE)
+        referrals=0, # LDAP REFERRALS (0 needed for AD)
+        search_filter='(uid=%(username)s)',  # ldap filter used for searching:
+                                             #search_filter = '(sAMAccountName=%(username)s)' # (AD)
+                                             #search_filter = '(uid=%(username)s)' # (OpenLDAP)
+                                             # you can also do more complex filtering like:
+                                             # "(&(cn=%(username)s)(memberOf=CN=WikiUsers,OU=Groups,DC=example,DC=org))"
+        # some attribute names we use to extract information from LDAP:
+        givenname_attribute=None, # ('givenName') ldap attribute we get the first name from
+        surname_attribute=None, # ('sn') ldap attribute we get the family name from
+        aliasname_attribute=None, # ('displayName') ldap attribute we get the aliasname from
+        email_attribute=None, # ('mail') ldap attribute we get the email address from
+        email_callback=None, # called to make up email address
+        coding='utf-8', # coding used for ldap queries and result values
+        timeout=10, # how long we wait for the ldap server [s]
+        verbose=True, # if True, put lots of LDAP debug info into the log
+        start_tls=0, # 0 = No, 1 = Try, 2 = Required
+        tls_cacertdir='',
+        tls_cacertfile='',
+        tls_certfile='',
+        tls_keyfile='',
+        tls_require_cert=0, # 0 == ldap.OPT_X_TLS_NEVER (needed for self-signed certs)
+        bind_once=False, # set to True to only do one bind - useful if configured to bind as the user on the first attempt
+        ):
+        self.server_uri = server_uri
+        self.bind_dn = bind_dn
+        self.bind_pw = bind_pw
+        self.base_dn = base_dn
+        self.scope = scope
+        self.referrals = referrals
+        self.search_filter = search_filter
+
+        self.givenname_attribute = givenname_attribute
+        self.surname_attribute = surname_attribute
+        self.aliasname_attribute = aliasname_attribute
+        self.email_attribute = email_attribute
+        self.email_callback = email_callback
+
+        self.coding = coding
+        self.timeout = timeout
+        self.verbose = verbose
+
+        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.bind_once = bind_once
+
+
     def login(self, request, user_obj, **kw):
         username = kw.get('username')
         password = kw.get('password')
         _ = request.getText
 
-        cfg = request.cfg
-        verbose = cfg.ldap_verbose
+        verbose = self.verbose
 
         # we require non-empty password as ldap bind does a anon (not password
         # protected) bind if the password is empty and SUCCEEDS!
@@ -55,32 +123,31 @@
             try:
                 u = None
                 dn = None
-                coding = cfg.ldap_coding
+                coding = self.coding
                 if verbose: logging.info("Setting misc. ldap options...")
                 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)
+                ldap.set_option(ldap.OPT_REFERRALS, self.referrals)
+                ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
 
-                starttls = cfg.ldap_start_tls
                 if hasattr(ldap, 'TLS_AVAIL') and 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_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:
                             ldap.set_option(option, value)
 
-                server = cfg.ldap_uri
+                server = self.server_uri
                 if verbose: logging.info("Trying to initialize %r." % server)
                 l = ldap.initialize(server)
                 if verbose: logging.info("Connected to LDAP server %r." % server)
 
-                if starttls and server.startswith('ldap:'):
+                if self.start_tls and server.startswith('ldap:'):
                     if verbose: logging.info("Trying to start TLS to %r." % server)
                     try:
                         l.start_tls_s()
@@ -90,22 +157,22 @@
                         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()
-                l.simple_bind_s(ldap_binddn.encode(coding), ldap_bindpw.encode(coding))
-                if verbose: logging.info("Bound with binddn %r" % ldap_binddn)
+                binddn = self.bind_dn % locals()
+                bindpw = self.bind_pw % locals()
+                l.simple_bind_s(binddn.encode(coding), bindpw.encode(coding))
+                if verbose: logging.info("Bound with binddn %r" % binddn)
 
                 # you can use %(username)s here to get the stuff entered in the form:
-                filterstr = cfg.ldap_filter % locals()
+                filterstr = self.search_filter % locals()
                 if verbose: logging.info("Searching %r" % filterstr)
-                attrs = [getattr(cfg, attr) for attr in [
-                                         'ldap_email_attribute',
-                                         'ldap_aliasname_attribute',
-                                         'ldap_surname_attribute',
-                                         'ldap_givenname_attribute',
-                                         ] if getattr(cfg, attr) is not None]
-                lusers = l.search_st(cfg.ldap_base, cfg.ldap_scope, filterstr.encode(coding),
-                                     attrlist=attrs, timeout=cfg.ldap_timeout)
+                attrs = [getattr(self, attr) for attr in [
+                                         'email_attribute',
+                                         'aliasname_attribute',
+                                         'surname_attribute',
+                                         'givenname_attribute',
+                                         ] if getattr(self, attr) is not None]
+                lusers = l.search_st(self.base_dn, self.scope, filterstr.encode(coding),
+                                     attrlist=attrs, timeout=self.timeout)
                 # we remove entries with dn == None to get the real result list:
                 lusers = [(dn, ldap_dict) for dn, ldap_dict in lusers if dn is not None]
                 if verbose:
@@ -123,27 +190,27 @@
                     return CancelLogin(_("Invalid username or password."))
 
                 dn, ldap_dict = lusers[0]
-                if not cfg.ldap_bindonce:
+                if not self.bind_once:
                     if verbose: logging.info("DN found is %r, trying to bind with pw" % dn)
                     l.simple_bind_s(dn, password.encode(coding))
                     if verbose: logging.info("Bound with dn %r (username: %r)" % (dn, username))
 
-                if cfg.ldap_email_callback is None:
-                    if cfg.ldap_email_attribute:
-                        email = ldap_dict.get(cfg.ldap_email_attribute, [''])[0].decode(coding)
+                if self.email_callback is None:
+                    if self.email_attribute:
+                        email = ldap_dict.get(self.email_attribute, [''])[0].decode(coding)
                     else:
                         email = None
                 else:
-                    email = cfg.ldap_email_callback(ldap_dict)
+                    email = self.email_callback(ldap_dict)
 
                 aliasname = ''
                 try:
-                    aliasname = ldap_dict[cfg.ldap_aliasname_attribute][0]
+                    aliasname = ldap_dict[self.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]
+                    sn = ldap_dict.get(self.surname_attribute, [''])[0]
+                    gn = ldap_dict.get(self.givenname_attribute, [''])[0]
                     if sn and gn:
                         aliasname = "%s, %s" % (sn, gn)
                     elif sn:
--- a/MoinMoin/config/multiconfig.py	Sat Apr 05 11:48:47 2008 +0200
+++ b/MoinMoin/config/multiconfig.py	Sat Apr 05 16:39:50 2008 +0200
@@ -352,64 +352,6 @@
     language_ignore_browser = False # ignore browser settings, use language_default
                                     # or user prefs
 
-    # 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.
-    # Be careful if you need a % char in those strings - as they are used as
-    # a format string, you have to write %% to get a single % in the end.
-    #ldap_binddn = 'binduser@example.org' # (AD)
-    #ldap_binddn = 'cn=admin,dc=example,dc=org' # (OpenLDAP)
-    #ldap_bindpw = 'secret'
-    # 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 = ''
-
-    # base DN we use for searching
-    #ldap_base = 'ou=SOMEUNIT,dc=example,dc=org'
-    ldap_base = ''
-
-    # 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)
-    # you can also do more complex filtering like:
-    # "(&(cn=%(username)s)(memberOf=CN=WikiUsers,OU=Groups,DC=example,DC=org))"
-
-    # some attribute names we use to extract information from LDAP:
-    ldap_givenname_attribute = None # ('givenName') ldap attribute we get the first name from
-    ldap_surname_attribute = None # ('sn') ldap attribute we get the family name from
-    ldap_aliasname_attribute = None # ('displayName') ldap attribute we get the aliasname from
-    ldap_email_attribute = None # ('mail') ldap attribute we get the email address from
-    ldap_email_callback = None # called to make up email address
-
-    ldap_coding = 'utf-8' # coding used for ldap queries and result values
-    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)
-
-    ldap_bindonce = False # set to True to only do one bind.  Useful if
-                          # configured to bind as the user on the first attempt
-
     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?