2015-04-15 21:13:14 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
|
|
|
#
|
|
|
|
# The MIT License (MIT)
|
|
|
|
# LdapCherry
|
|
|
|
# Copyright (c) 2014 Carpentier Pierre-Francois
|
|
|
|
|
2015-05-20 17:13:18 +02:00
|
|
|
import cherrypy
|
|
|
|
import ldap
|
2015-05-26 00:33:36 +02:00
|
|
|
import ldap.modlist as modlist
|
2015-05-20 17:13:18 +02:00
|
|
|
import logging
|
2015-05-20 14:21:43 +02:00
|
|
|
import ldapcherry.backend
|
2015-07-05 22:01:09 +02:00
|
|
|
import os
|
2015-05-26 00:33:36 +02:00
|
|
|
import re
|
|
|
|
|
|
|
|
class DelUserDontExists(Exception):
|
|
|
|
def __init__(self, user):
|
|
|
|
self.user = user
|
|
|
|
self.log = "cannot remove user, user <%(user)s> does not exist" % { 'user' : user}
|
|
|
|
|
2015-07-05 22:01:09 +02:00
|
|
|
class CaFileDontExist(Exception):
|
|
|
|
def __init__(self, cafile):
|
|
|
|
self.cafile = cafile
|
|
|
|
self.log = "CA file %(cafile)s don't exist" % { 'cafile': cafile }
|
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
NO_ATTR = 0
|
|
|
|
DISPLAYED_ATTRS = 1
|
|
|
|
LISTED_ATTRS = 2
|
|
|
|
ALL_ATTRS = 3
|
|
|
|
|
2015-04-15 21:13:14 +02:00
|
|
|
class Backend(ldapcherry.backend.Backend):
|
|
|
|
|
2015-05-31 18:40:35 +02:00
|
|
|
def __init__(self, config, logger, name, attrslist, key):
|
2015-05-20 17:13:18 +02:00
|
|
|
self.config = config
|
2015-05-21 08:52:06 +02:00
|
|
|
self._logger = logger
|
2015-05-21 19:55:11 +02:00
|
|
|
self.backend_name = name
|
2015-05-21 21:40:13 +02:00
|
|
|
self.binddn = self.get_param('binddn')
|
2015-05-22 01:16:53 +02:00
|
|
|
self.bindpassword = self.get_param('password')
|
2015-05-21 21:40:13 +02:00
|
|
|
self.ca = self.get_param('ca', False)
|
|
|
|
self.checkcert = self.get_param('checkcert', 'on')
|
|
|
|
self.starttls = self.get_param('starttls', 'off')
|
|
|
|
self.uri = self.get_param('uri')
|
2015-05-22 20:05:24 +02:00
|
|
|
self.timeout = self.get_param('timeout', 1)
|
2015-05-22 01:16:53 +02:00
|
|
|
self.userdn = self.get_param('userdn')
|
|
|
|
self.groupdn = self.get_param('groupdn')
|
2015-05-21 21:40:13 +02:00
|
|
|
self.user_filter_tmpl = self.get_param('user_filter_tmpl')
|
2015-05-25 18:52:14 +02:00
|
|
|
self.group_filter_tmpl = self.get_param('group_filter_tmpl')
|
|
|
|
self.search_filter_tmpl = self.get_param('search_filter_tmpl')
|
2015-05-26 00:33:36 +02:00
|
|
|
self.dn_user_attr = self.get_param('dn_user_attr')
|
|
|
|
self.objectclasses = []
|
2015-05-31 18:40:35 +02:00
|
|
|
self.key = key
|
2015-05-26 00:33:36 +02:00
|
|
|
for o in re.split('\W+', self.get_param('objectclasses')):
|
|
|
|
self.objectclasses.append(self._str(o))
|
2015-06-14 20:55:23 +02:00
|
|
|
self.group_attrs = {}
|
|
|
|
for param in config:
|
|
|
|
name, sep, group = param.partition('.')
|
|
|
|
if name == 'group_attr':
|
|
|
|
self.group_attrs[group] = self.get_param(param)
|
2015-05-26 00:33:36 +02:00
|
|
|
|
2015-05-22 20:05:24 +02:00
|
|
|
self.attrlist = []
|
|
|
|
for a in attrslist:
|
2015-05-26 00:33:36 +02:00
|
|
|
self.attrlist.append(self._str(a))
|
|
|
|
|
2015-06-28 19:54:19 +02:00
|
|
|
def _exception_handler(self, e):
|
|
|
|
et = type(e)
|
|
|
|
if et is ldap.OPERATIONS_ERROR:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "cannot use starttls with ldaps:// uri (uri: " + self.uri + ")",
|
|
|
|
)
|
|
|
|
elif et is ldap.INVALID_CREDENTIALS:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Configuration error, wrong credentials, unable to connect to ldap with '" + self.binddn + "'",
|
|
|
|
)
|
|
|
|
elif et is ldap.SERVER_DOWN:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Unable to contact ldap server '" + self.uri + "', check 'auth.ldap.uri' and ssl/tls configuration",
|
|
|
|
)
|
|
|
|
elif et is ldap.FILTER_ERROR:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Bad search filter, check '" + self.backend_name + ".*_filter_tmpl' params",
|
|
|
|
)
|
|
|
|
elif et is ldap.NO_SUCH_OBJECT:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Search DN '" + basedn \
|
|
|
|
+ "' doesn't exist, check '" \
|
|
|
|
+ self.backend_name + ".userdn' or '" \
|
|
|
|
+ self.backend_name + ".groupdn'",
|
|
|
|
)
|
|
|
|
elif et is ldap.OBJECT_CLASS_VIOLATION:
|
|
|
|
info = e[0]['info']
|
|
|
|
desc = e[0]['desc']
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Configuration error, " + desc + ", " + info,
|
|
|
|
)
|
|
|
|
elif et is ldap.INSUFFICIENT_ACCESS:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "Access error on '" + self.backend_name + "' backend, please check your acls in this backend",
|
|
|
|
)
|
|
|
|
elif et is ldap.ALREADY_EXISTS:
|
|
|
|
desc = e[0]['desc']
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "adding user failed, " + desc,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
self._logger(
|
|
|
|
severity = logging.ERROR,
|
|
|
|
msg = "unknow ldap exception in ldap backend",
|
|
|
|
)
|
|
|
|
raise e
|
|
|
|
|
2015-05-28 09:45:10 +02:00
|
|
|
def _connect(self):
|
|
|
|
ldap_client = ldap.initialize(self.uri)
|
2015-07-05 22:01:09 +02:00
|
|
|
ldap.set_option(ldap.OPT_REFERRALS, 0)
|
|
|
|
ldap.set_option(ldap.OPT_TIMEOUT, self.timeout)
|
2015-05-28 09:45:10 +02:00
|
|
|
if self.starttls == 'on':
|
2015-07-05 22:01:09 +02:00
|
|
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
2015-05-28 09:45:10 +02:00
|
|
|
else:
|
2015-07-05 22:01:09 +02:00
|
|
|
ldap.set_option(ldap.OPT_X_TLS_DEMAND, False)
|
2015-05-28 09:45:10 +02:00
|
|
|
if self.ca and self.checkcert == 'on':
|
2015-07-05 22:01:09 +02:00
|
|
|
if os.path.isfile(self.ca):
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca)
|
|
|
|
else:
|
|
|
|
raise CaFileDontExist(self.ca)
|
2015-05-28 09:45:10 +02:00
|
|
|
#else:
|
2015-07-05 22:01:09 +02:00
|
|
|
# ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '')
|
2015-05-28 09:45:10 +02:00
|
|
|
if self.checkcert == 'off':
|
2015-07-05 22:01:09 +02:00
|
|
|
# this is dark magic
|
|
|
|
# remove any of these two lines and it doesn't work
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
|
|
|
ldap_client.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
2015-05-28 09:45:10 +02:00
|
|
|
else:
|
2015-07-05 22:01:09 +02:00
|
|
|
# this is even darker magic
|
2015-06-17 19:22:57 +02:00
|
|
|
ldap_client.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
2015-07-05 22:01:09 +02:00
|
|
|
# it doesn't make sense to set it to never (don't check certifate)
|
|
|
|
# but it only works with this option... and it checks the certificat
|
|
|
|
# (I've lost my sanity over this)
|
|
|
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
2015-06-06 22:23:21 +02:00
|
|
|
if self.starttls == 'on':
|
2015-05-28 09:45:10 +02:00
|
|
|
try:
|
|
|
|
ldap_client.start_tls_s()
|
2015-06-28 19:54:19 +02:00
|
|
|
except Exception as e:
|
|
|
|
self._exception_handler(e)
|
2015-05-28 09:45:10 +02:00
|
|
|
return ldap_client
|
|
|
|
|
|
|
|
def _bind(self):
|
|
|
|
ldap_client = self._connect()
|
|
|
|
try:
|
|
|
|
ldap_client.simple_bind_s(self.binddn, self.bindpassword)
|
2015-06-28 19:54:19 +02:00
|
|
|
except Exception as e:
|
2015-05-28 09:45:10 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-06-28 19:54:19 +02:00
|
|
|
self._exception_handler(e)
|
2015-05-28 09:45:10 +02:00
|
|
|
return ldap_client
|
|
|
|
|
|
|
|
def _search(self, searchfilter, attrs, basedn):
|
2015-06-16 21:29:40 +02:00
|
|
|
if attrs == NO_ATTR:
|
|
|
|
attrlist = []
|
|
|
|
elif attrs == DISPLAYED_ATTRS:
|
|
|
|
# fix me later (to much attributes)
|
|
|
|
attrlist = self.attrlist
|
|
|
|
elif attrs == LISTED_ATTRS:
|
|
|
|
attrlist = self.attrlist
|
|
|
|
elif attrs == ALL_ATTRS:
|
|
|
|
attrlist = None
|
|
|
|
else:
|
|
|
|
attrlist = None
|
|
|
|
|
2015-05-28 09:45:10 +02:00
|
|
|
ldap_client = self._bind()
|
|
|
|
try:
|
|
|
|
r = ldap_client.search_s(basedn,
|
|
|
|
ldap.SCOPE_SUBTREE,
|
|
|
|
searchfilter,
|
2015-06-16 21:29:40 +02:00
|
|
|
attrlist=attrlist
|
2015-05-28 09:45:10 +02:00
|
|
|
)
|
2015-06-28 19:54:19 +02:00
|
|
|
except Exception as e:
|
2015-05-28 09:45:10 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-06-28 19:54:19 +02:00
|
|
|
self._exception_handler(e)
|
2015-05-28 09:45:10 +02:00
|
|
|
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
return r
|
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
def _get_user(self, username, attrs=ALL_ATTRS):
|
2015-05-28 09:45:10 +02:00
|
|
|
|
|
|
|
user_filter = self.user_filter_tmpl % {
|
|
|
|
'username': username
|
|
|
|
}
|
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
r = self._search(user_filter, attrs, self.userdn)
|
2015-05-28 09:45:10 +02:00
|
|
|
|
|
|
|
if len(r) == 0:
|
|
|
|
return None
|
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
if attrs == NO_ATTR:
|
2015-05-28 09:45:10 +02:00
|
|
|
dn_entry = r[0][0]
|
2015-06-16 21:29:40 +02:00
|
|
|
else:
|
|
|
|
dn_entry = r[0]
|
2015-05-28 09:45:10 +02:00
|
|
|
return dn_entry
|
|
|
|
|
2015-05-26 00:33:36 +02:00
|
|
|
def _str(self, s):
|
2015-06-27 22:35:34 +02:00
|
|
|
return s.encode('utf-8')
|
|
|
|
|
|
|
|
def _uni(self, s):
|
|
|
|
return s.decode('utf-8')
|
2015-05-20 17:13:18 +02:00
|
|
|
|
|
|
|
def auth(self, username, password):
|
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
binddn = self._get_user(username, NO_ATTR)
|
2015-05-24 15:11:49 +02:00
|
|
|
if not binddn is None:
|
2015-05-20 17:13:18 +02:00
|
|
|
ldap_client = self._connect()
|
|
|
|
try:
|
|
|
|
ldap_client.simple_bind_s(binddn, password)
|
|
|
|
except ldap.INVALID_CREDENTIALS:
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
return False
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2015-05-26 00:33:36 +02:00
|
|
|
def add_user(self, attrs):
|
|
|
|
ldap_client = self._bind()
|
|
|
|
attrs_str = {}
|
|
|
|
for a in attrs:
|
|
|
|
attrs_str[self._str(a)] = self._str(attrs[a])
|
|
|
|
attrs_str['objectClass'] = self.objectclasses
|
|
|
|
dn = self.dn_user_attr + '=' + attrs[self.dn_user_attr] + ',' + self.userdn
|
|
|
|
ldif = modlist.addModlist(attrs_str)
|
|
|
|
try:
|
|
|
|
ldap_client.add_s(dn,ldif)
|
2015-06-28 19:54:19 +02:00
|
|
|
except Exception as e:
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-06-28 19:54:19 +02:00
|
|
|
self._exception_handler(e)
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-20 17:13:18 +02:00
|
|
|
|
|
|
|
def del_user(self, username):
|
2015-05-26 00:33:36 +02:00
|
|
|
ldap_client = self._bind()
|
2015-06-16 21:29:40 +02:00
|
|
|
dn = self._get_user(username, NO_ATTR)
|
2015-05-26 00:33:36 +02:00
|
|
|
if not dn is None:
|
|
|
|
ldap_client.delete_s(dn)
|
|
|
|
else:
|
|
|
|
raise DelUserDontExists(username)
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
def set_attrs(self, username, attrs):
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client = self._bind()
|
2015-06-16 21:29:40 +02:00
|
|
|
tmp = self._get_user(username, ALL_ATTRS)
|
2015-06-14 20:55:23 +02:00
|
|
|
dn = tmp[0]
|
|
|
|
old_attrs = tmp[1]
|
|
|
|
for attr in attrs:
|
2015-06-16 21:29:40 +02:00
|
|
|
content = self._str(attrs[attr])
|
|
|
|
attr = self._str(attr)
|
2015-06-14 20:55:23 +02:00
|
|
|
new = { attr : content }
|
|
|
|
if attr in old_attrs:
|
|
|
|
old = { attr: old_attrs[attr]}
|
|
|
|
else:
|
2015-06-16 21:29:40 +02:00
|
|
|
old = {}
|
|
|
|
ldif = modlist.modifyModlist(old, new)
|
2015-06-28 19:54:19 +02:00
|
|
|
try:
|
|
|
|
ldap_client.modify_s(dn, ldif)
|
|
|
|
except Exception as e:
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
self._exception_handler(e)
|
2015-06-16 21:29:40 +02:00
|
|
|
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-24 17:32:03 +02:00
|
|
|
|
2015-06-16 21:58:44 +02:00
|
|
|
def add_to_groups(self, username, groups):
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client = self._bind()
|
2015-06-16 21:58:44 +02:00
|
|
|
tmp = self._get_user(username, ALL_ATTRS)
|
2015-06-14 20:55:23 +02:00
|
|
|
dn = tmp[0]
|
|
|
|
attrs = tmp[1]
|
|
|
|
attrs['dn'] = dn
|
|
|
|
for group in groups:
|
2015-06-16 23:56:12 +02:00
|
|
|
group = self._str(group)
|
2015-06-14 20:55:23 +02:00
|
|
|
for attr in self.group_attrs:
|
2015-06-16 23:56:12 +02:00
|
|
|
content = self._str(self.group_attrs[attr] % attrs)
|
2015-06-18 20:38:10 +02:00
|
|
|
self._logger(
|
|
|
|
severity = logging.DEBUG,
|
|
|
|
msg = "%(backend)s: adding user '%(user)s' with dn '%(dn)s' to group '%(group)s' by setting '%(attr)s' to '%(content)s'" % \
|
|
|
|
{ 'user': username, 'dn': dn, 'group': group, 'attr': attr, 'content': content, 'backend': self.backend_name }
|
|
|
|
)
|
2015-06-16 23:56:12 +02:00
|
|
|
ldif = modlist.modifyModlist({}, { attr : content })
|
|
|
|
try:
|
|
|
|
ldap_client.modify_s(group, ldif)
|
|
|
|
except ldap.TYPE_OR_VALUE_EXISTS as e:
|
2015-06-18 20:38:10 +02:00
|
|
|
self._logger(
|
|
|
|
severity = logging.INFO,
|
|
|
|
msg = "%(backend)s: user '%(user)s' already member of group '%(group)s' (attribute '%(attr)s')" % \
|
|
|
|
{ 'user': username, 'group': group, 'attr': attr, 'backend': self.backend_name}
|
|
|
|
)
|
2015-06-28 19:54:19 +02:00
|
|
|
except Exception as e:
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
self._exception_handler(e)
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
|
|
|
|
2015-06-16 21:58:44 +02:00
|
|
|
def del_from_groups(self, username, groups):
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client = self._bind()
|
2015-06-16 21:58:44 +02:00
|
|
|
tmp = self._get_user(username, ALL_ATTRS)
|
2015-06-14 20:55:23 +02:00
|
|
|
dn = tmp[0]
|
|
|
|
attrs = tmp[1]
|
|
|
|
attrs['dn'] = dn
|
|
|
|
for group in groups:
|
2015-06-16 23:56:12 +02:00
|
|
|
group = self._str(group)
|
2015-06-14 20:55:23 +02:00
|
|
|
for attr in self.group_attrs:
|
2015-06-16 23:56:12 +02:00
|
|
|
content = self._str(self.group_attrs[attr] % attrs)
|
|
|
|
ldif = [(ldap.MOD_DELETE, attr, content)]
|
|
|
|
try:
|
|
|
|
ldap_client.modify_s(group, ldif)
|
|
|
|
except ldap.NO_SUCH_ATTRIBUTE as e:
|
2015-06-28 19:54:19 +02:00
|
|
|
self._logger(
|
|
|
|
severity = logging.INFO,
|
|
|
|
msg = "%(backend)s: user '%(user)s' wasn't member of group '%(group)s' (attribute '%(attr)s')" % \
|
|
|
|
{ 'user': username, 'group': group, 'attr': attr, 'backend': self.backend_name}
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
ldap_client.unbind_s()
|
|
|
|
self._exception_handler(e)
|
2015-06-14 20:55:23 +02:00
|
|
|
ldap_client.unbind_s()
|
2015-05-25 19:52:54 +02:00
|
|
|
|
|
|
|
def search(self, searchstring):
|
2015-05-31 18:40:35 +02:00
|
|
|
ret = {}
|
2015-05-25 19:52:54 +02:00
|
|
|
|
|
|
|
searchfilter = self.search_filter_tmpl % {
|
|
|
|
'searchstring': searchstring
|
|
|
|
}
|
2015-06-16 21:29:40 +02:00
|
|
|
for u in self._search(searchfilter, DISPLAYED_ATTRS, self.userdn):
|
2015-05-31 18:40:35 +02:00
|
|
|
attrs = {}
|
|
|
|
attrs_tmp = u[1]
|
2015-06-06 22:23:21 +02:00
|
|
|
for attr in attrs_tmp:
|
2015-05-31 18:40:35 +02:00
|
|
|
value_tmp = attrs_tmp[attr]
|
|
|
|
if len(value_tmp) == 1:
|
2015-06-27 22:35:34 +02:00
|
|
|
attrs[attr] = self._uni(value_tmp[0])
|
2015-05-31 18:40:35 +02:00
|
|
|
else:
|
2015-06-27 22:35:34 +02:00
|
|
|
attrs[attr] = map(self._uni, value_tmp)
|
2015-06-06 22:23:21 +02:00
|
|
|
|
2015-05-31 18:40:35 +02:00
|
|
|
if self.key in attrs:
|
|
|
|
ret[attrs[self.key]] = attrs
|
2015-06-06 22:23:21 +02:00
|
|
|
return ret
|
2015-05-25 19:52:54 +02:00
|
|
|
|
2015-05-26 22:50:42 +02:00
|
|
|
def get_user(self, username):
|
|
|
|
ret = {}
|
2015-06-16 21:29:40 +02:00
|
|
|
attrs_tmp = self._get_user(username, ALL_ATTRS)[1]
|
2015-06-06 22:23:21 +02:00
|
|
|
for attr in attrs_tmp:
|
2015-05-26 22:50:42 +02:00
|
|
|
value_tmp = attrs_tmp[attr]
|
|
|
|
if len(value_tmp) == 1:
|
2015-06-27 22:35:34 +02:00
|
|
|
ret[attr] = self._uni(value_tmp[0])
|
2015-05-26 22:50:42 +02:00
|
|
|
else:
|
2015-06-27 22:35:34 +02:00
|
|
|
ret[attr] = map(self._uni, value_tmp)
|
2015-06-06 22:23:21 +02:00
|
|
|
return ret
|
2015-05-26 22:50:42 +02:00
|
|
|
|
2015-05-28 09:45:10 +02:00
|
|
|
def get_groups(self, username):
|
2015-06-16 21:29:40 +02:00
|
|
|
userdn = self._get_user(username, NO_ATTR)
|
2015-06-06 22:23:21 +02:00
|
|
|
|
2015-05-28 09:45:10 +02:00
|
|
|
searchfilter = self.group_filter_tmpl % {
|
|
|
|
'userdn': userdn,
|
2015-05-22 01:16:53 +02:00
|
|
|
'username': username
|
|
|
|
}
|
2015-05-20 17:13:18 +02:00
|
|
|
|
2015-06-16 21:29:40 +02:00
|
|
|
groups = self._search(searchfilter, NO_ATTR, self.groupdn)
|
2015-05-28 09:45:10 +02:00
|
|
|
ret = []
|
|
|
|
for entry in groups:
|
2015-06-27 22:35:34 +02:00
|
|
|
ret.append(self._uni(entry[0]))
|
2015-05-28 09:45:10 +02:00
|
|
|
return ret
|