# HG changeset patch # User Fabien Amarger # Date 1583921920 -3600 # Node ID ebf4806e4ab7ae4e121ad8ba094ba71819ee22be # Parent 2cc3f481ecd02a092162ef2c34d1dd205fb6bb82# Parent 3966f09d5f5c8702efa08fb2d3a8b29fad544189 merge public heads diff -r 3966f09d5f5c -r ebf4806e4ab7 .hgtags --- a/.hgtags Tue Mar 10 17:37:43 2020 +0100 +++ b/.hgtags Wed Mar 11 11:18:40 2020 +0100 @@ -647,3 +647,7 @@ 172f683a84f6dbc069298bba811f590afb5e5a43 3.26.14 e77900f19390fdf38515afdd212d21ac2592693d 3.27.0 e77900f19390fdf38515afdd212d21ac2592693d debian/3.27.0-1 +917601bb5b1ba13eb92296f5bd82eaa89e99bdad 3.27.1 +917601bb5b1ba13eb92296f5bd82eaa89e99bdad debian/3.27.1-1 +e731c31eaed06ac0a781db4d9a36d8b3732a4852 3.27.2 +e731c31eaed06ac0a781db4d9a36d8b3732a4852 debian/3.27.2-1 diff -r 3966f09d5f5c -r ebf4806e4ab7 cubicweb/__pkginfo__.py diff -r 3966f09d5f5c -r ebf4806e4ab7 cubicweb/server/hook.py diff -r 3966f09d5f5c -r ebf4806e4ab7 cubicweb/server/sources/ldapfeed.py --- a/cubicweb/server/sources/ldapfeed.py Tue Mar 10 17:37:43 2020 +0100 +++ b/cubicweb/server/sources/ldapfeed.py Wed Mar 11 11:18:40 2020 +0100 @@ -17,10 +17,6 @@ # with CubicWeb. If not, see . """cubicweb ldap feed source""" -from __future__ import division # XXX why? - -from datetime import datetime - import ldap3 from logilab.common.configuration import merge_options @@ -32,12 +28,11 @@ from cubicweb import _ # search scopes -BASE = ldap3.SEARCH_SCOPE_BASE_OBJECT -ONELEVEL = ldap3.SEARCH_SCOPE_SINGLE_LEVEL -SUBTREE = ldap3.SEARCH_SCOPE_WHOLE_SUBTREE -LDAP_SCOPES = {'BASE': BASE, - 'ONELEVEL': ONELEVEL, - 'SUBTREE': SUBTREE} +LDAP_SCOPES = { + 'BASE': ldap3.BASE, + 'ONELEVEL': ldap3.LEVEL, + 'SUBTREE': ldap3.SUBTREE, +} # map ldap protocol to their standard port PROTO_PORT = {'ldap': 389, @@ -117,6 +112,13 @@ 'help': 'additional filters to be set in the ldap query to find valid users', 'group': 'ldap-source', 'level': 2, }), + ('start-tls', + {'type': 'choice', + 'choices': ('true', 'false'), + 'default': 'false', + 'help': 'Start tls on connection (before bind)', + 'group': 'ldap-source', 'level': 1, + }), ('user-login-attr', {'type': 'string', 'default': 'uid', @@ -196,8 +198,9 @@ self._authenticate = getattr(self, '_auth_%s' % self.authmode) self.cnx_dn = typedconfig['data-cnx-dn'] self.cnx_pwd = typedconfig['data-cnx-password'] + self.start_tls = typedconfig['start-tls'] == "true" self.user_base_dn = str(typedconfig['user-base-dn']) - self.user_base_scope = globals()[typedconfig['user-scope']] + self.user_base_scope = LDAP_SCOPES[typedconfig['user-scope']] self.user_login_attr = typedconfig['user-login-attr'] self.user_default_groups = typedconfig['user-default-group'] self.user_attrs = {'dn': 'eid', 'modifyTimestamp': 'modification_date'} @@ -254,7 +257,7 @@ # check password by establishing a (unused) connection try: self._connect(user, password) - except ldap3.LDAPException as ex: + except ldap3.core.exceptions.LDAPException as ex: # Something went wrong, most likely bad credentials self.info('while trying to authenticate %s: %s', user, ex) raise AuthenticationError() @@ -270,18 +273,29 @@ def _connect(self, user=None, userpwd=None): protocol, host, port = self.connection_info() + kwargs = {} + if user: + kwargs['user'] = user['dn'] + elif self.cnx_dn: + kwargs['user'] = self.cnx_dn + if self.cnx_pwd: + kwargs['password'] = self.cnx_pwd self.info('connecting %s://%s:%s as %s', protocol, host, port, - user and user['dn'] or 'anonymous') + kwargs.get('user', 'anonymous')) server = ldap3.Server(host, port=int(port)) conn = ldap3.Connection( - server, user=user and user['dn'], - client_strategy=ldap3.STRATEGY_SYNC_RESTARTABLE, - auto_referrals=False) + server, client_strategy=ldap3.RESTARTABLE, auto_referrals=False, + raise_exceptions=True, + **kwargs) + if self.start_tls: + conn.start_tls() + # Now bind with the credentials given. Let exceptions propagate out. if user is None: - # XXX always use simple bind for data connection + # anonymous bind if not self.cnx_dn: - conn.bind() + if not conn.bind(): + raise AuthenticationError(conn.result["message"]) else: self._authenticate(conn, {'dn': self.cnx_dn}, self.cnx_pwd) else: @@ -292,7 +306,6 @@ return conn def _auth_simple(self, conn, user, userpwd): - conn.authentication = ldap3.AUTH_SIMPLE conn.user = user['dn'] conn.password = userpwd return conn.bind() @@ -317,7 +330,10 @@ if self._conn is None: self._conn = self._connect() ldapcnx = self._conn - if not ldapcnx.search(base, searchstr, search_scope=scope, attributes=attrs): + if self.start_tls: + ldapcnx.start_tls() + self.info("ldap start_tls started for %s", self.uri) + if not ldapcnx.search(base, searchstr, search_scope=scope, attributes=set(attrs) - {'dn'}): return [] result = [] for rec in ldapcnx.response: @@ -334,13 +350,13 @@ itemdict = {'dn': dn} for key, value in iterator: if self.user_attrs.get(key) == 'upassword': # XXx better password detection - value = value[0].encode('utf-8') + value = value[0] # we only support ldap_salted_sha1 for ldap sources, see: server/utils.py if not value.startswith(b'{SSHA}'): value = utils.crypt_password(value) itemdict[key] = Binary(value) elif self.user_attrs.get(key) == 'modification_date': - itemdict[key] = datetime.strptime(value[0], '%Y%m%d%H%M%SZ') + itemdict[key] = value else: if len(value) == 1: itemdict[key] = value = value[0] diff -r 3966f09d5f5c -r ebf4806e4ab7 cubicweb/server/test/unittest_ldapsource.py --- a/cubicweb/server/test/unittest_ldapsource.py Tue Mar 10 17:37:43 2020 +0100 +++ b/cubicweb/server/test/unittest_ldapsource.py Wed Mar 11 11:18:40 2020 +0100 @@ -143,12 +143,8 @@ @classmethod def pre_setup_database(cls, cnx, config): - if sys.version_info[:2] >= (3, 7): - raise unittest.SkipTest( - 'ldapfeed is not currently compatible with Python 3.7') cnx.create_entity('CWSource', name=u'ldap', type=u'ldapfeed', parser=u'ldapfeed', url=URL, config=CONFIG_LDAPFEED) - cnx.commit() return cls.pull(cnx) diff -r 3966f09d5f5c -r ebf4806e4ab7 cubicweb/web/action.py diff -r 3966f09d5f5c -r ebf4806e4ab7 debian/changelog --- a/debian/changelog Tue Mar 10 17:37:43 2020 +0100 +++ b/debian/changelog Wed Mar 11 11:18:40 2020 +0100 @@ -1,3 +1,15 @@ +cubicweb (3.27.2-1) unstable; urgency=medium + + * New upstream release + + -- Philippe Pepiot Thu, 05 Mar 2020 09:54:26 +0100 + +cubicweb (3.27.1-1) unstable; urgency=medium + + * New upstream release + + -- Katia Saurfelt Tue, 11 Feb 2020 10:51:08 +0100 + cubicweb (3.27.0-1) unstable; urgency=medium * New upstream release diff -r 3966f09d5f5c -r ebf4806e4ab7 doc/book/admin/ldap.rst --- a/doc/book/admin/ldap.rst Tue Mar 10 17:37:43 2020 +0100 +++ b/doc/book/admin/ldap.rst Wed Mar 11 11:18:40 2020 +0100 @@ -83,6 +83,8 @@ * `data-cnx-password`, password to use to open data connection to the ldap (eg used to respond to rql queries) +* `start-tls`, starting TLS before bind (valid values: "true", "false") + If the LDAP server accepts anonymous binds, then it is possible to leave data-cnx-dn and data-cnx-password empty. This is, however, quite unlikely in practice. Beware that the LDAP server might hide attributes diff -r 3966f09d5f5c -r ebf4806e4ab7 doc/tutorials/advanced/part02_security.rst diff -r 3966f09d5f5c -r ebf4806e4ab7 requirements/test-server.txt --- a/requirements/test-server.txt Tue Mar 10 17:37:43 2020 +0100 +++ b/requirements/test-server.txt Wed Mar 11 11:18:40 2020 +0100 @@ -1,2 +1,2 @@ psycopg2-binary -ldap3 < 2 +ldap3<3,>2