Mercurial > kallithea
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,