From 72917b4a827ec123d7d0fd3fcf6cb9259d3306fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Sun, 22 Nov 2020 18:13:24 +0000 Subject: [PATCH] [IMP] auth_ldap: allow multiple '%s' in the filter. Before this commit, the ldap filter can only contain a single placeholder, and thus the `login` can only be matched against a single LDAP attribute. This change allows matching the `login` against multiple LDAP attributes e.g. checking against either an email or a uid: (|(mail=%s)(uid=%s)) closes odoo/odoo#62134 Signed-off-by: Olivier Dony (odo) --- addons/auth_ldap/models/res_company_ldap.py | 45 ++++++++++++++------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/addons/auth_ldap/models/res_company_ldap.py b/addons/auth_ldap/models/res_company_ldap.py index 97b82a2e7a7..56779ed61b4 100644 --- a/addons/auth_ldap/models/res_company_ldap.py +++ b/addons/auth_ldap/models/res_company_ldap.py @@ -27,8 +27,24 @@ class CompanyLDAP(models.Model): "Leave empty to connect anonymously.") ldap_password = fields.Char(string='LDAP password', help="The password of the user account on the LDAP server that is used to query the directory.") - ldap_filter = fields.Char(string='LDAP filter', required=True) - ldap_base = fields.Char(string='LDAP base', required=True) + ldap_filter = fields.Char(string='LDAP filter', required=True, help="""\ + Filter used to look up user accounts in the LDAP database. It is an\ + arbitrary LDAP filter in string representation. Any `%s` placeholder\ + will be replaced by the login (identifier) provided by the user, the filter\ + should contain at least one such placeholder. + + The filter must result in exactly one (1) result, otherwise the login will\ + be considered invalid. + + Example (actual attributes depend on LDAP server and setup): + + (&(objectCategory=person)(objectClass=user)(sAMAccountName=%s)) + + or + + (|(mail=%s)(uid=%s)) + """) + ldap_base = fields.Char(string='LDAP base', required=True, help="DN of the user search scope: all descendants of this base will be searched for users.") user = fields.Many2one('res.users', string='Template User', help="User to copy when creating new users") create_user = fields.Boolean(default=True, @@ -79,19 +95,20 @@ class CompanyLDAP(models.Model): return connection def _get_entry(self, conf, login): - filter, dn, entry = False, False, False - try: - filter = filter_format(conf['ldap_filter'], (login,)) - except TypeError: - _logger.warning('Could not format LDAP filter. Your filter should contain one \'%s\'.') - if filter: - results = self._query(conf, tools.ustr(filter)) + filter_tmpl = conf['ldap_filter'] + placeholders = filter_tmpl.count('%s') + if not placeholders: + _logger.warning("LDAP filter %r contains no placeholder ('%%s').", filter_tmpl) - # Get rid of (None, attrs) for searchResultReference replies - results = [i for i in results if i[0]] - if len(results) == 1: - entry = results[0] - dn = results[0][0] + formatted_filter = filter_format(filter_tmpl, [login] * placeholders) + results = self._query(conf, formatted_filter) + + # Get rid of results (dn, attrs) without a dn + results = [entry for entry in results if entry[0]] + + dn, entry = False, False + if len(results) == 1: + dn, _ = entry = results[0] return dn, entry def _authenticate(self, conf, login, password):