Commit c6e5d56a authored by Philippe Pepiot's avatar Philippe Pepiot
Browse files

Merge 3.27

......@@ -647,3 +647,7 @@ e7eb914df71dc35c81515e8f04912678aa13c2b1 debian/3.26.11-2
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
......@@ -17,10 +17,6 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""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.server.sources import datafeed
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 @@ to respond to rql queries). Leave empty for anonymous bind',
'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 @@ You can set multiple groups by separating them by a comma.',
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 @@ You can set multiple groups by separating them by a comma.',
# 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 @@ You can set multiple groups by separating them by a comma.',
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 @@ You can set multiple groups by separating them by a comma.',
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 @@ You can set multiple groups by separating them by a comma.',
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 @@ You can set multiple groups by separating them by a comma.',
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]
......
......@@ -143,12 +143,8 @@ class LDAPFeedTestBase(CubicWebTC):
@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)
......
cubicweb (3.27.2-1) unstable; urgency=medium
* New upstream release
-- Philippe Pepiot <philippe.pepiot@logilab.fr> Thu, 05 Mar 2020 09:54:26 +0100
cubicweb (3.27.1-1) unstable; urgency=medium
* New upstream release
-- Katia Saurfelt <katia.saurfelt@logilab.fr> Tue, 11 Feb 2020 10:51:08 +0100
cubicweb (3.27.0-1) unstable; urgency=medium
* New upstream release
......
......@@ -83,6 +83,8 @@ LDAP server connection options:
* `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
......
psycopg2-binary
ldap3 < 2
ldap3<3,>2
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment