changeset 993:e80b2cbbd4ba beta

merge thayne ldap extensions
author Marcin Kuzminski <marcin@python-works.com>
date Fri, 04 Feb 2011 12:29:45 +0100
parents 7a1df0130533 (current diff) c03d16787b5c (diff)
children 7f9d23f6a526
files
diffstat 12 files changed, 324 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/docs/setup.rst	Tue Feb 01 15:19:42 2011 +0100
+++ b/docs/setup.rst	Fri Feb 04 12:29:45 2011 +0100
@@ -127,7 +127,7 @@
 -----------------------
 
 RhodeCode starting from version 1.1 supports ldap authentication. In order
-to use ldap, You have to install python-ldap package. This package is available
+to use LDAP, You have to install python-ldap_ package. This package is available
 via pypi, so You can install it by running
 
 ::
@@ -142,39 +142,181 @@
    python-ldap requires some certain libs on Your system, so before installing 
    it check that You have at least `openldap`, and `sasl` libraries.
 
-ldap settings are located in admin->ldap section,
+LDAP settings are located in admin->ldap section,
+
+This is a typical LDAP setup::
+
+ Connection settings
+ Enable LDAP          = checked
+ Host                 = host.example.org
+ Port                 = 389
+ Account              = <account>
+ Password             = <password>
+ Enable LDAPS         = checked
+ Certificate Checks   = DEMAND
+
+ Search settings
+ Base DN              = CN=users,DC=host,DC=example,DC=org
+ LDAP Filter          = (&(objectClass=user)(!(objectClass=computer)))
+ LDAP Search Scope    = SUBTREE
 
-Here's a typical ldap setup::
+ Attribute mappings
+ Login Attribute      = uid
+ First Name Attribute = firstName
+ Last Name Attribute  = lastName
+ E-mail Attribute     = mail
+
+.. _enable_ldap:
+
+Enable LDAP : required
+    Whether to use LDAP for authenticating users.
+
+.. _ldap_host:
+
+Host : required
+    LDAP server hostname or IP address.
+
+.. _Port:
+
+Port : required
+    389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
+
+.. _ldap_account:
 
- Enable ldap  = checked                 #controls if ldap access is enabled
- Host         = host.domain.org         #actual ldap server to connect
- Port         = 389 or 689 for ldaps    #ldap server ports
- Enable LDAPS = unchecked               #enable disable ldaps
- Account      = <account>               #access for ldap server(if required)
- Password     = <password>              #password for ldap server(if required)
- Base DN      = uid=%(user)s,CN=users,DC=host,DC=domain,DC=org
- 
+Account : optional
+    Only required if the LDAP server does not allow anonymous browsing of
+    records.  This should be a special account for record browsing.  This
+    will require `LDAP Password`_ below.
+
+.. _LDAP Password:
+
+Password : optional
+    Only required if the LDAP server does not allow anonymous browsing of
+    records.
+
+.. _Enable LDAPS:
+
+Enable LDAPS : optional
+    Check this if SSL encryption is necessary for communication with the
+    LDAP server - it will likely require `Port`_ to be set to a different
+    value (standard LDAPS port is 636).  When LDAPS is enabled then
+    `Certificate Checks`_ is required.
+
+.. _Certificate Checks:
 
-`Account` and `Password` are optional, and used for two-phase ldap 
-authentication so those are credentials to access Your ldap, if it doesn't 
-support anonymous search/user lookups. 
+Certificate Checks : optional
+    How SSL certificates verification is handled - this is only useful when
+    `Enable LDAPS`_ is enabled.  Only DEMAND or HARD offer full SSL security while
+    the other options are susceptible to man-in-the-middle attacks.  SSL
+    certificates can be installed to /etc/openldap/cacerts so that the
+    DEMAND or HARD options can be used with self-signed certificates or
+    certificates that do not have traceable certificates of authority.
+
+    NEVER
+        A serve certificate will never be requested or checked.
+
+    ALLOW
+        A server certificate is requested.  Failure to provide a
+        certificate or providing a bad certificate will not terminate the
+        session.
+
+    TRY
+        A server certificate is requested.  Failure to provide a
+        certificate does not halt the session; providing a bad certificate
+        halts the session.
+
+    DEMAND
+        A server certificate is requested and must be provided and
+        authenticated for the session to proceed.
 
-Base DN must have %(user)s template inside, it's a placer where Your uid used
-to login would go, it allows admins to specify not standard schema for uid 
-variable
+    HARD
+        The same as DEMAND.
+
+.. _Base DN:
+
+Base DN : required
+    The Distinguished Name (DN) where searches for users will be performed.
+    Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
+
+.. _LDAP Filter:
+
+LDAP Filter : optional
+    A LDAP filter defined by RFC 2254.  This is more useful when `LDAP
+    Search Scope`_ is set to SUBTREE.  The filter is useful for limiting
+    which LDAP objects are identified as representing Users for
+    authentication.  The filter is augmented by `Login Attribute`_ below.
+    This can commonly be left blank.
+
+.. _LDAP Search Scope:
+
+LDAP Search Scope : required
+    This limits how far LDAP will search for a matching object.
+
+    BASE
+        Only allows searching of `Base DN`_ and is usually not what you
+        want.
+
+    ONELEVEL
+        Searches all entries under `Base DN`_, but not Base DN itself.
+
+    SUBTREE
+        Searches all entries below `Base DN`_, but not Base DN itself.
+        When using SUBTREE `LDAP Filter`_ is useful to limit object
+        location.
+
+.. _Login Attribute:
 
-If all data are entered correctly, and `python-ldap` is properly installed
-Users should be granted to access RhodeCode wit ldap accounts. When 
-logging at the first time an special ldap account is created inside RhodeCode, 
-so You can control over permissions even on ldap users. If such user exists 
-already in RhodeCode database ldap user with the same username would be not 
-able to access RhodeCode.
+Login Attribute : required        
+    The LDAP record attribute that will be matched as the USERNAME or
+    ACCOUNT used to connect to RhodeCode.  This will be added to `LDAP
+    Filter`_ for locating the User object.  If `LDAP Filter`_ is specified as
+    "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
+    connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
+    ::
+
+        (&(LDAPFILTER)(uid=jsmith))
+
+.. _ldap_attr_firstname:
+
+First Name Attribute : required
+    The LDAP record attribute which represents the user's first name.
+
+.. _ldap_attr_lastname:
+
+Last Name Attribute : required
+    The LDAP record attribute which represents the user's last name.
+
+.. _ldap_attr_email:
+
+Email Attribute : required
+    The LDAP record attribute which represents the user's email address.
 
-If You have problems with ldap access and believe You entered correct 
-information check out the RhodeCode logs,any error messages sent from 
-ldap will be saved there.
+If all data are entered correctly, and python-ldap_ is properly installed
+users should be granted access to RhodeCode with ldap accounts.  At this
+time user information is copied from LDAP into the RhodeCode user database.
+This means that updates of an LDAP user object may not be reflected as a
+user update in RhodeCode.
+
+If You have problems with LDAP access and believe You entered correct
+information check out the RhodeCode logs, any error messages sent from LDAP
+will be saved there.
+
+Active Directory
+''''''''''''''''
 
+RhodeCode can use Microsoft Active Directory for user authentication.  This
+is done through an LDAP or LDAPS connection to Active Directory.  The
+following LDAP configuration settings are typical for using Active
+Directory ::
 
+ Base DN              = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
+ Login Attribute      = sAMAccountName
+ First Name Attribute = givenName
+ Last Name Attribute  = sn
+ E-mail Attribute     = mail
+
+All other LDAP settings will likely be site-specific and should be
+appropriately configured.
 
 Setting Up Celery
 -----------------
@@ -326,4 +468,5 @@
 .. _python: http://www.python.org/
 .. _mercurial: http://mercurial.selenic.com/
 .. _celery: http://celeryproject.org/
-.. _rabbitmq: http://www.rabbitmq.com/
\ No newline at end of file
+.. _rabbitmq: http://www.rabbitmq.com/
+.. _python-ldap: http://www.python-ldap.org/
--- a/rhodecode/controllers/admin/ldap_settings.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/controllers/admin/ldap_settings.py	Fri Feb 04 12:29:45 2011 +0100
@@ -48,15 +48,33 @@
 
 class LdapSettingsController(BaseController):
 
+    search_scope_choices = [('BASE',     _('BASE'),),
+                            ('ONELEVEL', _('ONELEVEL'),),
+                            ('SUBTREE',  _('SUBTREE'),),
+                            ]
+    search_scope_default = 'SUBTREE'
+
+    tls_reqcert_choices = [('NEVER',  _('NEVER'),),
+                           ('ALLOW',  _('ALLOW'),),
+                           ('TRY',    _('TRY'),),
+                           ('DEMAND', _('DEMAND'),),
+                           ('HARD',   _('HARD'),),
+                           ]
+    tls_reqcert_default = 'DEMAND'
+
     @LoginRequired()
     @HasPermissionAllDecorator('hg.admin')
     def __before__(self):
         c.admin_user = session.get('admin_user')
         c.admin_username = session.get('admin_username')
+        c.search_scope_choices = self.search_scope_choices
+        c.tls_reqcert_choices  = self.tls_reqcert_choices
         super(LdapSettingsController, self).__before__()
 
     def index(self):
         defaults = SettingsModel().get_ldap_settings()
+        c.search_scope_cur = defaults.get('ldap_search_scope')
+        c.tls_reqcert_cur  = defaults.get('ldap_tls_reqcert')
 
         return htmlfill.render(
                     render('admin/ldap/ldap.html'),
@@ -68,7 +86,8 @@
         """POST ldap create and store ldap settings"""
 
         settings_model = SettingsModel()
-        _form = LdapSettingsForm()()
+        _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
+                                 [x[0] for x in self.search_scope_choices])()
 
         try:
             form_result = _form.to_python(dict(request.POST))
@@ -91,6 +110,9 @@
 
         except formencode.Invalid, errors:
 
+            c.search_scope_cur = self.search_scope_default
+            c.tls_reqcert_cur  = self.search_scope_default
+
             return htmlfill.render(
                 render('admin/ldap/ldap.html'),
                 defaults=errors.value,
--- a/rhodecode/lib/auth.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/lib/auth.py	Fri Feb 04 12:29:45 2011 +0100
@@ -103,7 +103,7 @@
     user = user_model.get_by_username(username, cache=False)
 
     log.debug('Authenticating user using RhodeCode account')
-    if user is not None and user.is_ldap is False:
+    if user is not None and not user.ldap_dn:
         if user.active:
 
             if user.username == 'default' and user.active:
@@ -122,7 +122,7 @@
         user_obj = user_model.get_by_username(username, cache=False,
                                             case_insensitive=True)
 
-        if user_obj is not None and user_obj.is_ldap is False:
+        if user_obj is not None and not user_obj.ldap_dn:
             log.debug('this user already exists as non ldap')
             return False
 
@@ -141,15 +141,25 @@
                   'bind_dn':ldap_settings.get('ldap_dn_user'),
                   'bind_pass':ldap_settings.get('ldap_dn_pass'),
                   'use_ldaps':ldap_settings.get('ldap_ldaps'),
+                  'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
+                  'ldap_filter':ldap_settings.get('ldap_filter'),
+                  'search_scope':ldap_settings.get('ldap_search_scope'),
+                  'attr_login':ldap_settings.get('ldap_attr_login'),
                   'ldap_version':3,
                   }
             log.debug('Checking for ldap authentication')
             try:
                 aldap = AuthLdap(**kwargs)
-                res = aldap.authenticate_ldap(username, password)
-                log.debug('Got ldap response %s', res)
+                (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
+                log.debug('Got ldap DN response %s', user_dn)
 
-                if user_model.create_ldap(username, password):
+                user_attrs = {
+                    'name'     : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
+                    'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
+                    'email'    : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
+                    }
+
+                if user_model.create_ldap(username, password, user_dn, user_attrs):
                     log.info('created new ldap user')
 
                 return True
--- a/rhodecode/lib/auth_ldap.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/lib/auth_ldap.py	Fri Feb 04 12:29:45 2011 +0100
@@ -36,11 +36,15 @@
 class AuthLdap(object):
 
     def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
-                 use_ldaps=False, ldap_version=3):
+                 use_ldaps=False, tls_reqcert='DEMAND', ldap_version=3,
+                 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
+                 search_scope='SUBTREE',
+                 attr_login='uid'):
         self.ldap_version = ldap_version
         if use_ldaps:
             port = port or 689
         self.LDAP_USE_LDAPS = use_ldaps
+        self.TLS_REQCERT = ldap.__dict__['OPT_X_TLS_' + tls_reqcert]
         self.LDAP_SERVER_ADDRESS = server
         self.LDAP_SERVER_PORT = port
 
@@ -55,6 +59,10 @@
                                                self.LDAP_SERVER_PORT)
 
         self.BASE_DN = base_dn
+        self.LDAP_FILTER = ldap_filter
+        self.SEARCH_SCOPE = ldap.__dict__['SCOPE_' + search_scope]
+        self.attr_login = attr_login
+
 
     def authenticate_ldap(self, username, password):
         """Authenticate a user via LDAP and return his/her LDAP properties.
@@ -74,7 +82,13 @@
             raise LdapUsernameError("invalid character in username: ,")
         try:
             ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
+            ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
+            ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
+            ldap.set_option(ldap.OPT_TIMEOUT, 20)
             ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
+            ldap.set_option(ldap.OPT_TIMELIMIT, 15)
+            if self.LDAP_USE_LDAPS:
+                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
             server = ldap.initialize(self.LDAP_SERVER)
             if self.ldap_version == 2:
                 server.protocol = ldap.VERSION2
@@ -84,21 +98,29 @@
             if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
                 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
 
-            dn = self.BASE_DN % {'user':uid}
-            log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
-            server.simple_bind_s(dn, password)
+            filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, username)
+            log.debug("Authenticating %r filt %s at %s", self.BASE_DN, filt, self.LDAP_SERVER)
+            lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE, filt)
+
+            if not lobjects:
+                raise ldap.NO_SUCH_OBJECT()
 
-            properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
-            if not properties:
-                raise ldap.NO_SUCH_OBJECT()
+            for (dn, attrs) in lobjects:
+                try:
+                    server.simple_bind_s(dn, password)
+                    break
+
+                except ldap.INVALID_CREDENTIALS, e:
+                    log.debug("LDAP rejected password for user '%s' (%s): %s", uid, username, dn)
+
+                else:
+                    log.debug("No matching LDAP objecs for authentication of '%s' (%s)", uid, username)
+                    raise LdapPasswordError()
+
         except ldap.NO_SUCH_OBJECT, e:
             log.debug("LDAP says no such user '%s' (%s)", uid, username)
             raise LdapUsernameError()
-        except ldap.INVALID_CREDENTIALS, e:
-            log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
-            raise LdapPasswordError()
         except ldap.SERVER_DOWN, e:
             raise LdapConnectionError("LDAP can't access authentication server")
 
-        return properties[0]
-
+        return (dn, attrs)
--- a/rhodecode/lib/db_manage.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/lib/db_manage.py	Fri Feb 04 12:29:45 2011 +0100
@@ -336,7 +336,10 @@
 
         try:
             for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
-                      'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
+                      'ldap_tls_reqcert', 'ldap_dn_user', 'ldap_dn_pass',
+                      'ldap_base_dn', 'ldap_filter', 'ldap_search_scope',
+                      'ldap_attr_login', 'ldap_attr_firstname', 'ldap_attr_lastname',
+                      'ldap_attr_email']:
 
                 setting = RhodeCodeSettings(k, '')
                 self.sa.add(setting)
--- a/rhodecode/model/db.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/model/db.py	Fri Feb 04 12:29:45 2011 +0100
@@ -105,7 +105,7 @@
     lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
-    is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
+    ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
     user_log = relationship('UserLog', cascade='all')
     user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
--- a/rhodecode/model/forms.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/model/forms.py	Fri Feb 04 12:29:45 2011 +0100
@@ -334,23 +334,14 @@
             raise LdapImportError
         return value
 
-class BaseDnValidator(formencode.validators.FancyValidator):
+class AttrLoginValidator(formencode.validators.FancyValidator):
 
     def to_python(self, value, state):
 
-        try:
-            value % {'user':'valid'}
-
-            if value.find('%(user)s') == -1:
-                raise formencode.Invalid(_("You need to specify %(user)s in "
-                                           "template for example uid=%(user)s "
-                                           ",dc=company...") ,
-                                         value, state)
-
-        except KeyError:
-            raise formencode.Invalid(_("Wrong template used, only %(user)s "
-                                       "is an valid entry") ,
-                                         value, state)
+        if not value or not isinstance(value, (str, unicode)):
+            raise formencode.Invalid(_("The LDAP Login attribute of the CN must be specified "
+                                       "- this is the name of the attribute that is equivalent to 'username'"),
+                                     value, state)
 
         return value
 
@@ -521,7 +512,7 @@
     return _DefaultPermissionsForm
 
 
-def LdapSettingsForm():
+def LdapSettingsForm(tls_reqcert_choices, search_scope_choices):
     class _LdapSettingsForm(formencode.Schema):
         allow_extra_fields = True
         filter_extra_fields = True
@@ -530,8 +521,15 @@
         ldap_host = UnicodeString(strip=True,)
         ldap_port = Number(strip=True,)
         ldap_ldaps = StringBoolean(if_missing=False)
+        ldap_tls_reqcert = OneOf(tls_reqcert_choices)
         ldap_dn_user = UnicodeString(strip=True,)
         ldap_dn_pass = UnicodeString(strip=True,)
-        ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
+        ldap_base_dn = UnicodeString(strip=True,)
+        ldap_filter = UnicodeString(strip=True,)
+        ldap_search_scope = OneOf(search_scope_choices)
+        ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
+        ldap_attr_firstname = UnicodeString(strip=True,)
+        ldap_attr_lastname = UnicodeString(strip=True,)
+        ldap_attr_email = UnicodeString(strip=True,)
 
     return _LdapSettingsForm
--- a/rhodecode/model/settings.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/model/settings.py	Fri Feb 04 12:29:45 2011 +0100
@@ -72,10 +72,18 @@
         ldap_host
         ldap_port 
         ldap_ldaps
+        ldap_tls_reqcert
         ldap_dn_user 
         ldap_dn_pass 
         ldap_base_dn
+        ldap_filter
+        ldap_search_scope
+        ldap_attr_login
+        ldap_attr_firstname
+        ldap_attr_lastname
+        ldap_attr_email
         """
+        # ldap_search_scope
 
         r = self.sa.query(RhodeCodeSettings)\
                 .filter(RhodeCodeSettings.app_settings_name\
--- a/rhodecode/model/user.py	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/model/user.py	Fri Feb 04 12:29:45 2011 +0100
@@ -75,25 +75,27 @@
             self.sa.rollback()
             raise
 
-    def create_ldap(self, username, password):
+    def create_ldap(self, username, password, user_dn, attrs):
         """
         Checks if user is in database, if not creates this user marked
         as ldap user
         :param username:
         :param password:
+        :param user_dn:
+        :param attrs:
         """
         from rhodecode.lib.auth import get_crypt_password
         log.debug('Checking for such ldap account in RhodeCode database')
         if self.get_by_username(username, case_insensitive=True) is None:
             try:
                 new_user = User()
-                new_user.username = username.lower()#add ldap account always lowercase
+                new_user.username = username.lower() # add ldap account always lowercase
                 new_user.password = get_crypt_password(password)
-                new_user.email = '%s@ldap.server' % username
+                new_user.email = attrs['email']
                 new_user.active = True
-                new_user.is_ldap = True
-                new_user.name = '%s@ldap' % username
-                new_user.lastname = ''
+                new_user.ldap_dn = user_dn
+                new_user.name = attrs['name']
+                new_user.lastname = attrs['lastname']
 
 
                 self.sa.add(new_user)
--- a/rhodecode/templates/admin/ldap/ldap.html	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/templates/admin/ldap/ldap.html	Fri Feb 04 12:29:45 2011 +0100
@@ -21,13 +21,13 @@
     <div class="title">
         ${self.breadcrumbs()}       
     </div>
-    <h3>${_('LDAP administration')}</h3>
     ${h.form(url('ldap_settings'))}
     <div class="form">
         <div class="fields">
 
+	  <h3>${_('Connection settings')}</h3>
             <div class="field">
-                <div class="label label-checkbox"><label for="ldap_active">${_('Enable ldap')}</label></div>
+                <div class="label label-checkbox"><label for="ldap_active">${_('Enable LDAP')}</label></div>
                 <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_active',True,class_='small')}</div></div>
             </div>
             <div class="field">
@@ -39,10 +39,6 @@
                 <div class="input">${h.text('ldap_port',class_='small')}</div>
             </div>
             <div class="field">
-                <div class="label label-checkbox"><label for="ldap_ldaps">${_('Enable LDAPS')}</label></div>
-                <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_ldaps',True,class_='small')}</div></div>
-            </div>
-            <div class="field">
                 <div class="label"><label for="ldap_dn_user">${_('Account')}</label></div>
                 <div class="input">${h.text('ldap_dn_user',class_='small')}</div>
             </div>
@@ -51,9 +47,43 @@
                 <div class="input">${h.password('ldap_dn_pass',class_='small')}</div>
             </div>
             <div class="field">
+                <div class="label label-checkbox"><label for="ldap_ldaps">${_('Enable LDAPS')}</label></div>
+                <div class="checkboxes"><div class="checkbox">${h.checkbox('ldap_ldaps',True,class_='small')}</div></div>
+            </div>
+            <div class="field">
+                <div class="label"><label for="ldap_tls_reqcert">${_('Certificate Checks')}</label></div>
+                <div class="select">${h.select('ldap_tls_reqcert',c.tls_reqcert_cur,c.tls_reqcert_choices,class_='small')}</div>
+            </div>
+	  <h3>${_('Search settings')}</h3>
+            <div class="field">
                 <div class="label"><label for="ldap_base_dn">${_('Base DN')}</label></div>
                 <div class="input">${h.text('ldap_base_dn',class_='small')}</div>
             </div>
+            <div class="field">
+                <div class="label"><label for="ldap_filter">${_('LDAP Filter')}</label></div>
+                <div class="input">${h.text('ldap_filter',class_='small')}</div>
+            </div>
+            <div class="field">
+                <div class="label"><label for="ldap_search_scope">${_('LDAP Search Scope')}</label></div>
+                <div class="select">${h.select('ldap_search_scope',c.search_scope_cur,c.search_scope_choices,class_='small')}</div>
+            </div>
+	  <h3>${_('Attribute mappings')}</h3>
+            <div class="field">
+                <div class="label"><label for="ldap_attr_login">${_('Login Attribute')}</label></div>
+                <div class="input">${h.text('ldap_attr_login',class_='small')}</div>
+            </div>
+            <div class="field">
+                <div class="label"><label for="ldap_attr_firstname">${_('First Name Attribute')}</label></div>
+                <div class="input">${h.text('ldap_attr_firstname',class_='small')}</div>
+            </div>
+            <div class="field">
+                <div class="label"><label for="ldap_attr_lastname">${_('Last Name Attribute')}</label></div>
+                <div class="input">${h.text('ldap_attr_lastname',class_='small')}</div>
+            </div>
+            <div class="field">
+                <div class="label"><label for="ldap_attr_email">${_('E-mail Attribute')}</label></div>
+                <div class="input">${h.text('ldap_attr_email',class_='small')}</div>
+            </div>
             
             <div class="buttons">
             ${h.submit('save','Save',class_="ui-button")}
--- a/rhodecode/templates/admin/users/user_edit.html	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/templates/admin/users/user_edit.html	Fri Feb 04 12:29:45 2011 +0100
@@ -49,6 +49,15 @@
             
              <div class="field">
                 <div class="label">
+                    <label for="ldap_dn">${_('LDAP DN')}:</label>
+                </div>
+                <div class="input">
+                    ${h.text('ldap_dn',class_='small')}
+                </div>
+             </div>
+            
+             <div class="field">
+                <div class="label">
                     <label for="new_password">${_('New password')}:</label>
                 </div>
                 <div class="input">
@@ -231,4 +240,4 @@
         });
 </script>    
 </div>
-</%def>  
\ No newline at end of file
+</%def>
--- a/rhodecode/templates/admin/users/users.html	Tue Feb 01 15:19:42 2011 +0100
+++ b/rhodecode/templates/admin/users/users.html	Fri Feb 04 12:29:45 2011 +0100
@@ -49,7 +49,7 @@
                     <td>${user.last_login}</td>
                     <td>${h.bool2icon(user.active)}</td>
                     <td>${h.bool2icon(user.admin)}</td>
-                    <td>${h.bool2icon(user.is_ldap)}</td>
+                    <td>${h.bool2icon(bool(user.ldap_dn))}</td>
                     <td>
                         ${h.form(url('user', id=user.user_id),method='delete')}
                             ${h.submit('remove_','delete',id="remove_user_%s" % user.user_id,