根据 Django 中的登录用户名获取 LDAP 组
Get LDAP group based on login username in Django
我需要在 Django 中使用 LDAP 查询来检索用户所在组的列表。用例是:用户填写他的 LDAP 登录凭据,Django 将验证他的身份并分配他的 LDAP 组.
用户登录完美无缺,但我无法使用 memberUid=%(user)s
过滤群组。 我正在寻找将用户名作为过滤器参数传递给 "groupfilter" 属性 的正确方法。我用 LDAP 应用程序浏览器测试了查询,似乎工作正常。
- OS: Ubuntu 14.04 x64
- Python版本:2.7.6
- Django 版本: (1, 6, 11, 'final', 0)
RatticWeb LDAP 配置
我正在使用 RatticWeb django application to manage users' passwords with the following local configuration (see also Rattic repository wiki):
conf/local.cfg
[ldap]
requirecert = True
# LDAP server details
uri = ldap://localhost:port
# Bind DN
binddn = cn=admin,dc=mydomain
bindpw = mypassword
# User parameters
userbase = ou=Users,dc=mydomain
userfilter = (uid=%(user)s)
#userfilter = (objectClass=person)
userfirstname = givenName
userlastname = sn
useldapgroups = True
# Set up the basic group parameters.
groupbase = ou=Groups,dc=mydomain
groupfilter = (&(objectClass=posixGroup)(memberUid=%(user)s))
grouptype = PosixGroupType
# How do I find staff
#staff = cn=staff,ou=groups,dc=example,dc=com
loglevel = DEBUG
此配置文件由settings.py读取(source code):
ratticweb/settings.py
# Defaults to a bogus filter so that searching yields no errors in the log
AUTH_LDAP_GROUP_FILTER = confget('ldap', 'groupfilter', '(objectClass=_fake)')
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_BASE, ldap.SCOPE_SUBTREE, AUTH_LDAP_USER_FILTER)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_BASE, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_FILTER)
日志
现在,我需要过滤传递来自用户登录表单的用户名参数的 LDAP 组,我尝试使用与 userfilter 相同的格式,但服务器返回以下内容错误:
[Wed Jan 13 16:51:24.257325 2016] [:error] [pid 11280] 2016-01-13 16:51:24,256 [ERROR] Caught Exception while authenticating myusername
[Wed Jan 13 16:51:24.257438 2016] [:error] [pid 11280] Traceback (most recent call last):
[Wed Jan 13 16:51:24.257478 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 323, in authenticate
[Wed Jan 13 16:51:24.257515 2016] [:error] [pid 11280] self._get_or_create_user()
[Wed Jan 13 16:51:24.257550 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 539, in _get_or_create_user
[Wed Jan 13 16:51:24.257586 2016] [:error] [pid 11280] self._mirror_groups()
[Wed Jan 13 16:51:24.257620 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 638, in _mirror_groups
[Wed Jan 13 16:51:24.257697 2016] [:error] [pid 11280] group_names = self._get_groups().get_group_names()
[Wed Jan 13 16:51:24.257734 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 755, in get_group_names
[Wed Jan 13 16:51:24.257770 2016] [:error] [pid 11280] group_infos = self._get_group_infos()
[Wed Jan 13 16:51:24.257804 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 803, in _get_group_infos
[Wed Jan 13 16:51:24.257835 2016] [:error] [pid 11280] self._group_search)
[Wed Jan 13 16:51:24.257864 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py", line 374, in user_groups
[Wed Jan 13 16:51:24.257893 2016] [:error] [pid 11280] groups = search.execute(ldap_user.connection)
[Wed Jan 13 16:51:24.257928 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py", line 150, in execute
[Wed Jan 13 16:51:24.257957 2016] [:error] [pid 11280] filterstr = self.filterstr % filterargs
[Wed Jan 13 16:51:24.257990 2016] [:error] [pid 11280] TypeError: format requires a mapping
编辑:错误原因
LDAPSearch
的构造函数填充其属性如下:
self.base_dn = 'groupbase = ou=Groups,dc=mydomain'
self.scope = ldap.SCOPE_SUBTREE
self.filterstr = '(&(objectClass=posixGroup)(memberUid=%(user)s))'
/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py #150
def execute(self, connection, filterargs=()):
"""
Executes the search on the given connection (an LDAPObject). filterargs
is an object that will be used for expansion of the filter string.
The python-ldap library returns utf8-encoded strings. For the sake of
sanity, this method will decode all result strings and return them as
Unicode.
"""
try:
filterstr = self.filterstr % filterargs
results = connection.search_s(self.base_dn.encode('utf-8'),
self.scope,
filterstr.encode('utf-8'))
except ldap.LDAPError, e:
results = []
logger.error(u"search_s('%s', %d, '%s') raised %s" %
(self.base_dn, self.scope, filterstr, pprint.pformat(e)))
return self._process_results(results)
最后,我找到了解决方案:
conf/local.cfg
[ldap]
....
# Set up the basic group parameters.
groupbase = ou=Groups,dc=mydomain
groupfilter = (objectClass=posixGroup)
grouptype = PosixGroupType
...
关键因素是 groupType PosixGroupType
的使用,因为 Django Auth LDAP 会根据 memberUid 参数过滤对群组的查询(观察 user_groups
函数的 else 部分:
class PosixGroupType(LDAPGroupType):
"""
An LDAPGroupType subclass that handles groups of class posixGroup.
"""
def user_groups(self, ldap_user, group_search):
"""
Searches for any group that is either the user's primary or contains the
user as a member.
"""
groups = []
try:
user_uid = ldap_user.attrs['uid'][0]
if 'gidNumber' in ldap_user.attrs:
user_gid = ldap_user.attrs['gidNumber'][0]
filterstr = u'(|(gidNumber=%s)(memberUid=%s))' % (
self.ldap.filter.escape_filter_chars(user_gid),
self.ldap.filter.escape_filter_chars(user_uid)
)
else:
filterstr = u'(memberUid=%s)' % (
self.ldap.filter.escape_filter_chars(user_uid),
)
search = group_search.search_with_additional_term_string(filterstr)
groups = search.execute(ldap_user.connection)
except (KeyError, IndexError):
pass
return groups
我需要在 Django 中使用 LDAP 查询来检索用户所在组的列表。用例是:用户填写他的 LDAP 登录凭据,Django 将验证他的身份并分配他的 LDAP 组.
用户登录完美无缺,但我无法使用 memberUid=%(user)s
过滤群组。 我正在寻找将用户名作为过滤器参数传递给 "groupfilter" 属性 的正确方法。我用 LDAP 应用程序浏览器测试了查询,似乎工作正常。
- OS: Ubuntu 14.04 x64
- Python版本:2.7.6
- Django 版本: (1, 6, 11, 'final', 0)
RatticWeb LDAP 配置
我正在使用 RatticWeb django application to manage users' passwords with the following local configuration (see also Rattic repository wiki):
conf/local.cfg
[ldap]
requirecert = True
# LDAP server details
uri = ldap://localhost:port
# Bind DN
binddn = cn=admin,dc=mydomain
bindpw = mypassword
# User parameters
userbase = ou=Users,dc=mydomain
userfilter = (uid=%(user)s)
#userfilter = (objectClass=person)
userfirstname = givenName
userlastname = sn
useldapgroups = True
# Set up the basic group parameters.
groupbase = ou=Groups,dc=mydomain
groupfilter = (&(objectClass=posixGroup)(memberUid=%(user)s))
grouptype = PosixGroupType
# How do I find staff
#staff = cn=staff,ou=groups,dc=example,dc=com
loglevel = DEBUG
此配置文件由settings.py读取(source code):
ratticweb/settings.py
# Defaults to a bogus filter so that searching yields no errors in the log
AUTH_LDAP_GROUP_FILTER = confget('ldap', 'groupfilter', '(objectClass=_fake)')
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_BASE, ldap.SCOPE_SUBTREE, AUTH_LDAP_USER_FILTER)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_BASE, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_FILTER)
日志
现在,我需要过滤传递来自用户登录表单的用户名参数的 LDAP 组,我尝试使用与 userfilter 相同的格式,但服务器返回以下内容错误:
[Wed Jan 13 16:51:24.257325 2016] [:error] [pid 11280] 2016-01-13 16:51:24,256 [ERROR] Caught Exception while authenticating myusername
[Wed Jan 13 16:51:24.257438 2016] [:error] [pid 11280] Traceback (most recent call last):
[Wed Jan 13 16:51:24.257478 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 323, in authenticate
[Wed Jan 13 16:51:24.257515 2016] [:error] [pid 11280] self._get_or_create_user()
[Wed Jan 13 16:51:24.257550 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 539, in _get_or_create_user
[Wed Jan 13 16:51:24.257586 2016] [:error] [pid 11280] self._mirror_groups()
[Wed Jan 13 16:51:24.257620 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 638, in _mirror_groups
[Wed Jan 13 16:51:24.257697 2016] [:error] [pid 11280] group_names = self._get_groups().get_group_names()
[Wed Jan 13 16:51:24.257734 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 755, in get_group_names
[Wed Jan 13 16:51:24.257770 2016] [:error] [pid 11280] group_infos = self._get_group_infos()
[Wed Jan 13 16:51:24.257804 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/backend.py", line 803, in _get_group_infos
[Wed Jan 13 16:51:24.257835 2016] [:error] [pid 11280] self._group_search)
[Wed Jan 13 16:51:24.257864 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py", line 374, in user_groups
[Wed Jan 13 16:51:24.257893 2016] [:error] [pid 11280] groups = search.execute(ldap_user.connection)
[Wed Jan 13 16:51:24.257928 2016] [:error] [pid 11280] File "/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py", line 150, in execute
[Wed Jan 13 16:51:24.257957 2016] [:error] [pid 11280] filterstr = self.filterstr % filterargs
[Wed Jan 13 16:51:24.257990 2016] [:error] [pid 11280] TypeError: format requires a mapping
编辑:错误原因
LDAPSearch
的构造函数填充其属性如下:
self.base_dn = 'groupbase = ou=Groups,dc=mydomain'
self.scope = ldap.SCOPE_SUBTREE
self.filterstr = '(&(objectClass=posixGroup)(memberUid=%(user)s))'
/usr/local/lib/python2.7/dist-packages/django_auth_ldap/config.py #150
def execute(self, connection, filterargs=()):
"""
Executes the search on the given connection (an LDAPObject). filterargs
is an object that will be used for expansion of the filter string.
The python-ldap library returns utf8-encoded strings. For the sake of
sanity, this method will decode all result strings and return them as
Unicode.
"""
try:
filterstr = self.filterstr % filterargs
results = connection.search_s(self.base_dn.encode('utf-8'),
self.scope,
filterstr.encode('utf-8'))
except ldap.LDAPError, e:
results = []
logger.error(u"search_s('%s', %d, '%s') raised %s" %
(self.base_dn, self.scope, filterstr, pprint.pformat(e)))
return self._process_results(results)
最后,我找到了解决方案:
conf/local.cfg
[ldap]
....
# Set up the basic group parameters.
groupbase = ou=Groups,dc=mydomain
groupfilter = (objectClass=posixGroup)
grouptype = PosixGroupType
...
关键因素是 groupType PosixGroupType
的使用,因为 Django Auth LDAP 会根据 memberUid 参数过滤对群组的查询(观察 user_groups
函数的 else 部分:
class PosixGroupType(LDAPGroupType):
"""
An LDAPGroupType subclass that handles groups of class posixGroup.
"""
def user_groups(self, ldap_user, group_search):
"""
Searches for any group that is either the user's primary or contains the
user as a member.
"""
groups = []
try:
user_uid = ldap_user.attrs['uid'][0]
if 'gidNumber' in ldap_user.attrs:
user_gid = ldap_user.attrs['gidNumber'][0]
filterstr = u'(|(gidNumber=%s)(memberUid=%s))' % (
self.ldap.filter.escape_filter_chars(user_gid),
self.ldap.filter.escape_filter_chars(user_uid)
)
else:
filterstr = u'(memberUid=%s)' % (
self.ldap.filter.escape_filter_chars(user_uid),
)
search = group_search.search_with_additional_term_string(filterstr)
groups = search.execute(ldap_user.connection)
except (KeyError, IndexError):
pass
return groups