mirror of
https://github.com/kakwa/ldapcherry
synced 2024-11-22 09:24:21 +01:00
many comment + encoding fixes
This commit is contained in:
parent
383c68f8b8
commit
6ee0ff0354
@ -37,6 +37,8 @@ ALL_ATTRS = 3
|
|||||||
class Backend(ldapcherry.backend.Backend):
|
class Backend(ldapcherry.backend.Backend):
|
||||||
|
|
||||||
def __init__(self, config, logger, name, attrslist, key):
|
def __init__(self, config, logger, name, attrslist, key):
|
||||||
|
"""Backend initialization"""
|
||||||
|
# Recover all the parameters
|
||||||
self.config = config
|
self.config = config
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self.backend_name = name
|
self.backend_name = name
|
||||||
@ -55,6 +57,8 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
self.dn_user_attr = self.get_param('dn_user_attr')
|
self.dn_user_attr = self.get_param('dn_user_attr')
|
||||||
self.objectclasses = []
|
self.objectclasses = []
|
||||||
self.key = key
|
self.key = key
|
||||||
|
# objectclasses parameter is a coma separated list in configuration
|
||||||
|
# split it to get a real list, and convert it to bytes
|
||||||
for o in re.split('\W+', self.get_param('objectclasses')):
|
for o in re.split('\W+', self.get_param('objectclasses')):
|
||||||
self.objectclasses.append(self._str(o))
|
self.objectclasses.append(self._str(o))
|
||||||
self.group_attrs = {}
|
self.group_attrs = {}
|
||||||
@ -67,7 +71,9 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
for a in attrslist:
|
for a in attrslist:
|
||||||
self.attrlist.append(self._str(a))
|
self.attrlist.append(self._str(a))
|
||||||
|
|
||||||
|
# exception handler (mainly to log something meaningful)
|
||||||
def _exception_handler(self, e):
|
def _exception_handler(self, e):
|
||||||
|
""" Exception handling"""
|
||||||
et = type(e)
|
et = type(e)
|
||||||
if et is ldap.OPERATIONS_ERROR:
|
if et is ldap.OPERATIONS_ERROR:
|
||||||
self._logger(
|
self._logger(
|
||||||
@ -117,7 +123,8 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
severity=logging.ERROR,
|
severity=logging.ERROR,
|
||||||
msg="Access error on '" +
|
msg="Access error on '" +
|
||||||
self.backend_name +
|
self.backend_name +
|
||||||
"' backend, please check your acls in this backend",
|
"' backend, please check your acls in backend " +
|
||||||
|
self.backend_name,
|
||||||
)
|
)
|
||||||
elif et is ldap.ALREADY_EXISTS:
|
elif et is ldap.ALREADY_EXISTS:
|
||||||
desc = e[0]['desc']
|
desc = e[0]['desc']
|
||||||
@ -128,11 +135,12 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.ERROR,
|
severity=logging.ERROR,
|
||||||
msg="unknow ldap exception in ldap backend",
|
msg="unknow exception in backend " + self.backend_name,
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
|
"""Initialize an ldap client"""
|
||||||
ldap_client = ldap.initialize(self.uri)
|
ldap_client = ldap.initialize(self.uri)
|
||||||
ldap.set_option(ldap.OPT_REFERRALS, 0)
|
ldap.set_option(ldap.OPT_REFERRALS, 0)
|
||||||
ldap.set_option(ldap.OPT_TIMEOUT, self.timeout)
|
ldap.set_option(ldap.OPT_TIMEOUT, self.timeout)
|
||||||
@ -140,7 +148,9 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldap.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
||||||
else:
|
else:
|
||||||
ldap.set_option(ldap.OPT_X_TLS_DEMAND, False)
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, False)
|
||||||
|
# set the CA file if declared and if necessary
|
||||||
if self.ca and self.checkcert == 'on':
|
if self.ca and self.checkcert == 'on':
|
||||||
|
# check if the CA file actually exists
|
||||||
if os.path.isfile(self.ca):
|
if os.path.isfile(self.ca):
|
||||||
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca)
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca)
|
||||||
else:
|
else:
|
||||||
@ -176,6 +186,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return ldap_client
|
return ldap_client
|
||||||
|
|
||||||
def _bind(self):
|
def _bind(self):
|
||||||
|
"""bind to the ldap with the technical account"""
|
||||||
ldap_client = self._connect()
|
ldap_client = self._connect()
|
||||||
try:
|
try:
|
||||||
ldap_client.simple_bind_s(self.binddn, self.bindpassword)
|
ldap_client.simple_bind_s(self.binddn, self.bindpassword)
|
||||||
@ -185,6 +196,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return ldap_client
|
return ldap_client
|
||||||
|
|
||||||
def _search(self, searchfilter, attrs, basedn):
|
def _search(self, searchfilter, attrs, basedn):
|
||||||
|
"""Generic search"""
|
||||||
if attrs == NO_ATTR:
|
if attrs == NO_ATTR:
|
||||||
attrlist = []
|
attrlist = []
|
||||||
elif attrs == DISPLAYED_ATTRS:
|
elif attrs == DISPLAYED_ATTRS:
|
||||||
@ -197,6 +209,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
attrlist = None
|
attrlist = None
|
||||||
|
|
||||||
|
# bind and search the ldap
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
try:
|
try:
|
||||||
r = ldap_client.search_s(
|
r = ldap_client.search_s(
|
||||||
@ -211,7 +224,10 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
|
|
||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
# reencode everything in utf-8
|
# python-ldap doesn't know utf-8,
|
||||||
|
# it treates everything as bytes.
|
||||||
|
# So it's necessary to reencode
|
||||||
|
# it's output in utf-8.
|
||||||
ret = []
|
ret = []
|
||||||
for entry in r:
|
for entry in r:
|
||||||
uni_dn = self._uni(entry[0])
|
uni_dn = self._uni(entry[0])
|
||||||
@ -228,6 +244,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _get_user(self, username, attrs=ALL_ATTRS):
|
def _get_user(self, username, attrs=ALL_ATTRS):
|
||||||
|
"""Get a user from the ldap"""
|
||||||
|
|
||||||
username = ldap.filter.escape_filter_chars(username)
|
username = ldap.filter.escape_filter_chars(username)
|
||||||
user_filter = self.user_filter_tmpl % {
|
user_filter = self.user_filter_tmpl % {
|
||||||
@ -239,23 +256,33 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
if len(r) == 0:
|
if len(r) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# if NO_ATTR, only return the DN
|
||||||
if attrs == NO_ATTR:
|
if attrs == NO_ATTR:
|
||||||
dn_entry = r[0][0]
|
dn_entry = r[0][0]
|
||||||
|
# in other cases, return everything (dn + attributes)
|
||||||
else:
|
else:
|
||||||
dn_entry = r[0]
|
dn_entry = r[0]
|
||||||
return dn_entry
|
return dn_entry
|
||||||
|
|
||||||
|
# python-ldap talks in bytes,
|
||||||
|
# as the rest of ldapcherry talks in unicode utf-8:
|
||||||
|
# * everything passed to python-ldap must be converted to bytes
|
||||||
|
# * everything coming from python-ldap must be converted to unicode
|
||||||
|
|
||||||
def _str(self, s):
|
def _str(self, s):
|
||||||
|
"""unicode -> bytes conversion"""
|
||||||
if s is None:
|
if s is None:
|
||||||
return None
|
return None
|
||||||
return s.encode('utf-8')
|
return s.encode('utf-8')
|
||||||
|
|
||||||
def _uni(self, s):
|
def _uni(self, s):
|
||||||
|
"""bytes -> unicode conversion"""
|
||||||
if s is None:
|
if s is None:
|
||||||
return None
|
return None
|
||||||
return s.decode('utf-8', 'ignore')
|
return s.decode('utf-8', 'ignore')
|
||||||
|
|
||||||
def auth(self, username, password):
|
def auth(self, username, password):
|
||||||
|
"""Authentication of a user"""
|
||||||
|
|
||||||
binddn = self._str(self._get_user(username, NO_ATTR))
|
binddn = self._str(self._get_user(username, NO_ATTR))
|
||||||
if binddn is not None:
|
if binddn is not None:
|
||||||
@ -271,18 +298,22 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def add_user(self, attrs):
|
def add_user(self, attrs):
|
||||||
|
"""add a user"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
attrs_str = {}
|
attrs_str = {}
|
||||||
|
# encoding crap
|
||||||
for a in attrs:
|
for a in attrs:
|
||||||
attrs_str[self._str(a)] = self._str(attrs[a])
|
attrs_str[self._str(a)] = self._str(attrs[a])
|
||||||
|
|
||||||
attrs_str['objectClass'] = self.objectclasses
|
attrs_str['objectClass'] = self.objectclasses
|
||||||
|
# construct is DN
|
||||||
dn = \
|
dn = \
|
||||||
self._str(self.dn_user_attr) +\
|
self._str(self.dn_user_attr) +\
|
||||||
'=' +\
|
'=' +\
|
||||||
self._str(attrs[self.dn_user_attr]) +\
|
self._str(attrs[self.dn_user_attr]) +\
|
||||||
',' +\
|
',' +\
|
||||||
self._str(self.userdn)
|
self._str(self.userdn)
|
||||||
|
# gen the ldif fir add_s and add the user
|
||||||
ldif = modlist.addModlist(attrs_str)
|
ldif = modlist.addModlist(attrs_str)
|
||||||
try:
|
try:
|
||||||
ldap_client.add_s(dn, ldif)
|
ldap_client.add_s(dn, ldif)
|
||||||
@ -292,15 +323,20 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def del_user(self, username):
|
def del_user(self, username):
|
||||||
|
"""delete a user"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
|
# recover the user dn
|
||||||
dn = self._str(self._get_user(username, NO_ATTR))
|
dn = self._str(self._get_user(username, NO_ATTR))
|
||||||
|
# delete
|
||||||
if dn is not None:
|
if dn is not None:
|
||||||
ldap_client.delete_s(dn)
|
ldap_client.delete_s(dn)
|
||||||
else:
|
else:
|
||||||
|
ldap_client.unbind_s()
|
||||||
raise DelUserDontExists(username)
|
raise DelUserDontExists(username)
|
||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def set_attrs(self, username, attrs):
|
def set_attrs(self, username, attrs):
|
||||||
|
""" Set user attributes"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
tmp = self._get_user(username, ALL_ATTRS)
|
tmp = self._get_user(username, ALL_ATTRS)
|
||||||
dn = self._str(tmp[0])
|
dn = self._str(tmp[0])
|
||||||
@ -319,6 +355,8 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
[[(battr, bcontent, 1)]] + ldap.dn.str2dn(dn)[1:]
|
[[(battr, bcontent, 1)]] + ldap.dn.str2dn(dn)[1:]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# if attr is already set, replace the value
|
||||||
|
# (see dict old passed to modifyModlist)
|
||||||
if attr in old_attrs:
|
if attr in old_attrs:
|
||||||
if type(old_attrs[attr]) is list:
|
if type(old_attrs[attr]) is list:
|
||||||
tmp = []
|
tmp = []
|
||||||
@ -328,6 +366,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
bold_value = self._str(old_attrs[attr])
|
bold_value = self._str(old_attrs[attr])
|
||||||
old = {battr: bold_value}
|
old = {battr: bold_value}
|
||||||
|
# attribute is not set, just add it
|
||||||
else:
|
else:
|
||||||
old = {}
|
old = {}
|
||||||
ldif = modlist.modifyModlist(old, new)
|
ldif = modlist.modifyModlist(old, new)
|
||||||
@ -341,14 +380,18 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
|
|
||||||
def add_to_groups(self, username, groups):
|
def add_to_groups(self, username, groups):
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
|
# recover dn of the user and his attributes
|
||||||
tmp = self._get_user(username, ALL_ATTRS)
|
tmp = self._get_user(username, ALL_ATTRS)
|
||||||
dn = tmp[0]
|
dn = tmp[0]
|
||||||
attrs = tmp[1]
|
attrs = tmp[1]
|
||||||
attrs['dn'] = dn
|
attrs['dn'] = dn
|
||||||
dn = self._str(tmp[0])
|
dn = self._str(tmp[0])
|
||||||
|
# add user to all groups
|
||||||
for group in groups:
|
for group in groups:
|
||||||
group = self._str(group)
|
group = self._str(group)
|
||||||
|
# iterate on group membership attributes
|
||||||
for attr in self.group_attrs:
|
for attr in self.group_attrs:
|
||||||
|
# fill the content template
|
||||||
content = self._str(self.group_attrs[attr] % attrs)
|
content = self._str(self.group_attrs[attr] % attrs)
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.DEBUG,
|
severity=logging.DEBUG,
|
||||||
@ -357,7 +400,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
" setting '%(attr)s' to '%(content)s'" % {
|
" setting '%(attr)s' to '%(content)s'" % {
|
||||||
'user': username,
|
'user': username,
|
||||||
'dn': self._uni(dn),
|
'dn': self._uni(dn),
|
||||||
'group': group,
|
'group': self._uni(group),
|
||||||
'attr': attr,
|
'attr': attr,
|
||||||
'content': self._uni(content),
|
'content': self._uni(content),
|
||||||
'backend': self.backend_name
|
'backend': self.backend_name
|
||||||
@ -366,6 +409,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldif = modlist.modifyModlist({}, {attr: content})
|
ldif = modlist.modifyModlist({}, {attr: content})
|
||||||
try:
|
try:
|
||||||
ldap_client.modify_s(group, ldif)
|
ldap_client.modify_s(group, ldif)
|
||||||
|
# if already member, not a big deal, just log it and continue
|
||||||
except ldap.TYPE_OR_VALUE_EXISTS as e:
|
except ldap.TYPE_OR_VALUE_EXISTS as e:
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.INFO,
|
severity=logging.INFO,
|
||||||
@ -373,7 +417,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
" already member of group '%(group)s'"
|
" already member of group '%(group)s'"
|
||||||
"(attribute '%(attr)s')" % {
|
"(attribute '%(attr)s')" % {
|
||||||
'user': username,
|
'user': username,
|
||||||
'group': group,
|
'group': self._uni(group),
|
||||||
'attr': attr,
|
'attr': attr,
|
||||||
'backend': self.backend_name
|
'backend': self.backend_name
|
||||||
}
|
}
|
||||||
@ -386,6 +430,9 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def del_from_groups(self, username, groups):
|
def del_from_groups(self, username, groups):
|
||||||
|
"""Delete user from groups"""
|
||||||
|
# it follows the same logic than add_to_groups
|
||||||
|
# but with MOD_DELETE
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
tmp = self._get_user(username, ALL_ATTRS)
|
tmp = self._get_user(username, ALL_ATTRS)
|
||||||
dn = tmp[0]
|
dn = tmp[0]
|
||||||
@ -406,7 +453,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
" wasn't member of group '%(group)s'"
|
" wasn't member of group '%(group)s'"
|
||||||
" (attribute '%(attr)s')" % {
|
" (attribute '%(attr)s')" % {
|
||||||
'user': username,
|
'user': username,
|
||||||
'group': group,
|
'group': self._uni(group),
|
||||||
'attr': attr,
|
'attr': attr,
|
||||||
'backend': self.backend_name
|
'backend': self.backend_name
|
||||||
}
|
}
|
||||||
@ -417,12 +464,16 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def search(self, searchstring):
|
def search(self, searchstring):
|
||||||
ret = {}
|
"""Search users"""
|
||||||
|
# escape special char to avoid injection
|
||||||
searchstring = ldap.filter.escape_filter_chars(searchstring)
|
searchstring = ldap.filter.escape_filter_chars(searchstring)
|
||||||
|
# fill the search string template
|
||||||
searchfilter = self.search_filter_tmpl % {
|
searchfilter = self.search_filter_tmpl % {
|
||||||
'searchstring': searchstring
|
'searchstring': searchstring
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
# search an process the result a little
|
||||||
for u in self._search(searchfilter, DISPLAYED_ATTRS, self.userdn):
|
for u in self._search(searchfilter, DISPLAYED_ATTRS, self.userdn):
|
||||||
attrs = {}
|
attrs = {}
|
||||||
attrs_tmp = u[1]
|
attrs_tmp = u[1]
|
||||||
@ -438,6 +489,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_user(self, username):
|
def get_user(self, username):
|
||||||
|
"""Gest a specific user"""
|
||||||
ret = {}
|
ret = {}
|
||||||
tmp = self._get_user(username, ALL_ATTRS)
|
tmp = self._get_user(username, ALL_ATTRS)
|
||||||
if tmp is None:
|
if tmp is None:
|
||||||
@ -452,7 +504,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_groups(self, username):
|
def get_groups(self, username):
|
||||||
|
"""Get all groups of a user"""
|
||||||
username = ldap.filter.escape_filter_chars(username)
|
username = ldap.filter.escape_filter_chars(username)
|
||||||
userdn = self._get_user(username, NO_ATTR)
|
userdn = self._get_user(username, NO_ATTR)
|
||||||
|
|
||||||
@ -464,5 +516,5 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
groups = self._search(searchfilter, NO_ATTR, self.groupdn)
|
groups = self._search(searchfilter, NO_ATTR, self.groupdn)
|
||||||
ret = []
|
ret = []
|
||||||
for entry in groups:
|
for entry in groups:
|
||||||
ret.append(self._uni(entry[0]))
|
ret.append(entry[0])
|
||||||
return ret
|
return ret
|
||||||
|
Loading…
Reference in New Issue
Block a user