如何使用Python连接ldap

好多使用ldap认证的软件都是Python的,比如superset和airflow, 好吧,他们都是airbnb家的。在配置ldap的时候可能会出现认证失败,你不知道是因为什么导致配置失败的。所以,就要

跟踪源码,看看内部怎么认证实现的。

ldap介绍和使用安装参见: https://www.cnblogs.com/woshimrf/p/ldap.html

登录的源码参见: https://github.com/apache/airflow/blob/70e937a8d8ff308a9fb9055ceb7ef2c034200b36/airflow/contrib/auth/backends/ldap_auth.py#L191

具体来实现如下:

为了模拟环境,我们使用docker-python。基于Debian Python3: https://github.com/Ryan-Miao/docker-china-source/tree/master/docker-python

启动

docker run -it ryan/python:3 /bin/bash

下载ldap3

pip install ldap3

测试连接

root@5edee218d962:/# python
Python 3.7.4 (default, Jul 13 2019, 14:20:24)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from ldap3 import Server, Connection, ALL, NTLM, ALL_ATTRIBUTES, LEVEL, SUBTREE
>>> server = Server('172.17.0.2', get_info=ALL)
>>> conn = Connection(server, 'cn=admin,dc=demo,dc=com', 'admin', auto_bind=True)
>>> conn.extend.standard.who_am_i()
'dn:cn=admin,dc=demo,dc=com'

测试登录部分

登录源码如下:

@staticmethod
def try_login(username, password):
conn = get_ldap_connection(configuration.conf.get("ldap", "bind_user"),
configuration.conf.get("ldap", "bind_password")) search_filter = "(&({0})({1}={2}))".format(
configuration.conf.get("ldap", "user_filter"),
configuration.conf.get("ldap", "user_name_attr"),
username
) search_scope = LEVEL
if configuration.conf.has_option("ldap", "search_scope"):
if configuration.conf.get("ldap", "search_scope") == "SUBTREE":
search_scope = SUBTREE
else:
search_scope = LEVEL # todo: BASE or ONELEVEL? res = conn.search(configuration.conf.get("ldap", "basedn"), search_filter, search_scope=search_scope) # todo: use list or result?
if not res:
log.info("Cannot find user %s", username)
raise AuthenticationError("Invalid username or password") entry = conn.response[0] conn.unbind() if 'dn' not in entry:
# The search filter for the user did not return any values, so an
# invalid user was used for credentials.
raise AuthenticationError("Invalid username or password") try:
conn = get_ldap_connection(entry['dn'], password)
except KeyError:
log.error("""
Unable to parse LDAP structure. If you're using Active Directory
and not specifying an OU, you must set search_scope=SUBTREE in airflow.cfg.
%s
""", traceback.format_exc())
raise LdapException(
"Could not parse LDAP structure. "
"Try setting search_scope in airflow.cfg, or check logs"
) if not conn:
log.info("Password incorrect for user %s", username)
raise AuthenticationError("Invalid username or password")

第一步: 获取连接

from ldap3 import Server, Connection, ALL, NTLM, ALL_ATTRIBUTES, LEVEL, SUBTREE

server = Server('172.17.0.2:389', get_info=ALL)
conn = Connection(server, 'cn=admin,dc=demo,dc=com', 'admin', auto_bind=True)
conn.extend.standard.who_am_i()

第二步: 根据filter search用户。 这里我们的配置文件如下:

[ldap]
# set this to ldaps://<your.ldap.server>:<port>
uri = ldap://172.17.0.2:389
user_filter = objectClass=inetOrgPerson
user_name_attr = sn
group_member_attr = memberOf
superuser_filter =
data_profiler_filter =
bind_user = cn=admin,dc=demo,dc=com
bind_password = admin
basedn = dc=demo,dc=com
cacert =
search_scope = SUBTREE

源码就是拼接filter, 最后变成(&(objectClass=inetOrgPerson)(sn=ryanmiao)), 然后search scope.


>>> with Connection(server, 'cn=admin,dc=demo,dc=com', 'admin') as conn:
... conn.search('dc=demo,dc=com', '(&(objectClass=inetOrgPerson)(cn=hr-ryan))', search_scope=SUBTREE)
... entry = conn.entries[0]
...
True
>>> entry
DN: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com - STATUS: Read - READ TIME: 2019-08-19T12:58:34.966181

第三步: 从上一步得到dn,然后根据用户输入的密码,再次连接, 不抛异常就证明密码正确

>>> conn = Connection(server, 'cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com', '123456', auto_bind=True)
>>>
>>> conn = Connection(server, 'cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com', '1234567', auto_bind=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.7/site-packages/ldap3/core/connection.py", line 325, in __init__
self.do_auto_bind()
File "/usr/local/lib/python3.7/site-packages/ldap3/core/connection.py", line 353, in do_auto_bind
raise LDAPBindError(self.last_error)
ldap3.core.exceptions.LDAPBindError: automatic bind not successful - invalidCredentials

测试分组

ldap提供了分组,配置见前文。我们采用ldap统一登录之后,还要把用户放入不同的group里来区分权限。比如splunk-users, splunk-admin, gitlab-admin, gitlab-user等。

根据用户名和密码,我们实现了用户登录密码验证,接下来需要取到用户所属于的group。

源码如下:


def groups_user(conn, search_base, user_filter, user_name_att, username):
search_filter = "(&({0})({1}={2}))".format(user_filter, user_name_att, username)
try:
memberof_attr = configuration.conf.get("ldap", "group_member_attr")
except Exception:
memberof_attr = "memberOf"
res = conn.search(search_base, search_filter, attributes=[memberof_attr])
if not res:
log.info("Cannot find user %s", username)
raise AuthenticationError("Invalid username or password") if conn.response and memberof_attr not in conn.response[0]["attributes"]:
log.warning("""Missing attribute "%s" when looked-up in Ldap database.
The user does not seem to be a member of a group and therefore won't see any dag
if the option filter_by_owner=True and owner_mode=ldapgroup are set""",
memberof_attr)
return [] user_groups = conn.response[0]["attributes"][memberof_attr] regex = re.compile("cn=([^,]*).*", re.IGNORECASE)
groups_list = []
try:
groups_list = [regex.search(i).group(1) for i in user_groups]
except IndexError:
log.warning("Parsing error when retrieving the user's group(s)."
" Check if the user belongs to at least one group"
" or if the user's groups name do not contain special characters") return groups_list

同样,第一步,连接,第二步search,但需要返回字段memberof。注意我们前面配置了group_member_attr=memberof

>>> with Connection(server, 'cn=admin,dc=demo,dc=com', 'admin') as conn:
... conn.search('dc=demo,dc=com', '(&(objectClass=inetOrgPerson)(sn=hr-ryan))', attributes=['memberof'])
... entry = conn.entries[0]
... entry
...
True
DN: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com - STATUS: Read - READ TIME: 2019-08-19T13:05:20.592805
memberOf: cn=g-admin,ou=Group,dc=demo,dc=com
cn=g-users,ou=Group,dc=demo,dc=com
cn=g-a,ou=Group,dc=demo,dc=com

如果取不到group会报错。

以上就差不多是airflow的ldap配置原理了。其他雷同,不一样的地方也许是在filter的地方,我们找对应软件的源码look一下就ok了。

如何使用Python连接ldap的更多相关文章

  1. python实现ldap接入

    需要提前安装python-ldap模块 python接入ldap其实分了几个步骤: 1.使用一个管理员账户登陆到ldap 2.使用一个字段值是唯一的字段,去搜索到要验证用户的DN值(ldap搜索到的单 ...

  2. 【初学python】使用python连接mysql数据查询结果并显示

    因为测试工作经常需要与后台数据库进行数据比较和统计,所以采用python编写连接数据库脚本方便测试,提高工作效率,脚本如下(python连接mysql需要引入第三方库MySQLdb,百度下载安装) # ...

  3. python连接mysql的驱动

    对于py2.7的朋友,直接可以用MySQLdb去连接,但是MySQLdb不支持python3.x.这是需要注意的~ 那应该用什么python连接mysql的驱动呢,在stackoverflow上有人解 ...

  4. paip. 解决php 以及 python 连接access无效的参数量。参数不足,期待是 1”的错误

    paip. 解决php 以及 python 连接access无效的参数量.参数不足,期待是 1"的错误 作者Attilax  艾龙,  EMAIL:1466519819@qq.com  来源 ...

  5. python 连接sql server

    linux 下pymssql模块的安装 所需压缩包:pymssql-2.1.0.tar.bz2freetds-patched.tar.gz 安装: tar -xvf pymssql-2.1.0.tar ...

  6. paip.python连接mysql最佳实践o4

    paip.python连接mysql最佳实践o4 python连接mysql 还使用了不少时间...,相比php困难多了..麻烦的.. 而php,就容易的多兰.. python标准库没mysql库,只 ...

  7. python连接字符串的方式

    发现Python连接字符串又是用的不顺手,影响速度 1.数字对字符进行拼接 s=""  #定义这个字符串,方便做连接 print type(s) for i in range(10 ...

  8. python连接zookeeper的日志问题

    用python连接zookeeper时,在终端里,一直会有zookeeper的日志冒出来,这样会很烦. -- ::,:(: Exceeded deadline by 11ms 解决方法是在连接后设置一 ...

  9. python 连接Mysql数据库

    1.下载http://dev.mysql.com/downloads/connector/python/ 由于Python安装的是3.4,所以需要下载下面的mysql-connector-python ...

随机推荐

  1. NOIP2018&2013提高组T1暨洛谷P5019 铺设道路

    题目链接:https://www.luogu.org/problemnew/show/P5019 花絮:普及蒟蒻终于A了一道提高的题目?emm,写一篇题解纪念一下吧.求过! 分析: 这道题我们可以采用 ...

  2. C#3.0新增功能10 表达式树 03 支持表达式树的框架类型

    连载目录    [已更新最新开发文章,点击查看详细] 存在可与表达式树配合使用的 .NET Core framework 中的类的大型列表. 可以在 System.Linq.Expressions 查 ...

  3. Javascript中style,currentStyle和getComputedStyle的区别以及获取css操作方法

    style: 只能获取行内style. 调用:obj.style.属性; 兼容:都兼容 currentStyle: 可以获取该obj所有style,但只可读. 调用:obj.currentStyle[ ...

  4. hdu6375 度度熊学队列

    度度熊学队列 题目传送门 解题思路 STL大法好.直接用deque,但是N的范围很大,如果直接开那么多的deque会爆内存,所以用map< int, deque< int>>, ...

  5. linux初学者-网络桥接篇

    linux初学者-网络桥接篇 在网络的使用中,有时需要搭建网络桥来实现网络桥接.例如在一台主机上制作一台虚拟机,虚拟机是没有物理网卡的,这时虚拟机数据的发送和接收就需要通过主机上的物理网卡,需要主机的 ...

  6. k8s1.9.0安装--完整集群部署

    三.完整集群部署 - kubernetes-with-ca 1. 理解认证授权 1.1 为什么要认证 想理解认证,我们得从认证解决什么问题.防止什么问题的发生入手.防止什么问题呢?是防止有人入侵你的集 ...

  7. SSM整合环境搭建demo

    1.项目目录结构 2.项目中用的jar包 3.web.xml(其中主要配置spring与springmvc) <?xml version="1.0" encoding=&qu ...

  8. python网络爬虫(14)使用Scrapy搭建爬虫框架

    目的意义 爬虫框架也许能简化工作量,提高效率等.scrapy是一款方便好用,拓展方便的框架. 本文将使用scrapy框架,示例爬取自己博客中的文章内容. 说明 学习和模仿来源:https://book ...

  9. Linux基础之特殊权限

    22.5)特殊权限 22.5.1)SetUID(简称suid)(数字权限是4000) 命令功能: 临时使用命令的属主权限执行该命令.即如果文件有suid权限时,那么普通用户去执行该文件时,会以该文件的 ...

  10. mybatis动态插入数据库

    <insert id="dynamicAddUser"> insert into t_user <!-- trim 对所有的表中列名 进行动态处理 --> ...