博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用包ldap3进行Python的LDAP操作
阅读量:7047 次
发布时间:2019-06-28

本文共 10872 字,大约阅读时间需要 36 分钟。

hot3.png

背景:我的管理平台系统需要在权限验证方面与ldap集成,所以需要使用python操作LDAP。

本来想使用python-ldap,但是安装过程中出现如下问题,而且花费很多时间精力没有解决该问题,所以放弃python-ldap,而使用ldap3。

E:\>pip install python-ldapCollecting python-ldapD:\app\Python27\lib\site-packages\pip\_vendor\requests\packages\urllib3\util\ssl_.py:318: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This maycause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.  SNIMissingWarning省略。。。    C:\Users\zhaoxp2\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -DHAVE_SASL -DHAVE_TLS -DHAVE_LIBLDAP_R -DHAVE_LIBLDAP_R -DLDAPMODULE_VERSION=2.4.27 -IModules -I/usr/include -I/usr/include/sasl -I/usr/local/include -I/usr/local/include/sasl -ID:\app\Python27\include -ID:\app\Python27\PC /TcModules/LDAPObject.c /Fobuild\temp.win-amd64-2.7\Release\Modules/LDAPObject.obj    LDAPObject.c    c:\users\zhaoxp2\appdata\local\temp\pip-build-kecixv\python-ldap\modules\errors.h(8) : fatal error C1083: Cannot open include file: 'lber.h': No such file or directory    error: command 'C:\\Users\\zhaoxp2\\AppData\\Local\\Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\VC\\Bin\\amd64\\cl.exe' failed with exit status 2    ----------------------------------------Command "D:\app\Python27\python.exe -u -c "import setuptools, tokenize;__file__='c:\\users\\zhaoxp2\\appdata\\local\\temp\\pip-build-kecixv\\python-ldap\\setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record c:\users\zhaoxp2\appdata\local\temp\pip-f4qvel-record\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in c:\users\zhaoxp2\appdata\local\temp\pip-build-kecixv\python-ldap\

安装ldap3

安装ldap3很简单,pip install ldap3即可。地址:

为什么使用ldap3?下面是给出的原因:

A strictly RFC 4510 conforming LDAP V3 pure Python client library. The same codebase works with Python 2, Python 3, PyPy, PyPy3 and Nuikta.

使用ldap3访问LDAP服务器

接下来是ldap3的使用介绍。

参考:

3种连接方法:

1 anonymous

2 simple password

3 SASL(simple authentication and security layer)

这里注意文档中说明了longin就是bind(后面代码中会有相关参数)。

在API的Connection中定义了5中策略:

  策略 同步否? Return
1 SYNC synchronous True/False
2 ASYNC asynchronous Integer
3 LDIF    
4 RESTARTABLE synchronous True/False
5 REUSABLE asynchronous Integer

在异步模式下,将返回message_id而不是True或者False。另外异步模式下将会调用Connection的get_response(message_id)方法。还可以设置timeout参数。

LDIF模式用来创建LDIF-CHANGEs流。The LDIF strategy is used to create a stream of LDIF-CHANGEs.

默认模式是SYNC。

连接:

>>> server = Server('ipa.demo1.freeipa.org')>>> conn = Connection(server)>>> conn.bind()True

或者

>> conn = Connection('ipa.demo1.freeipa.org', auto_bind=True)True

查看连接信息:

print serverprint conn

获取服务器信息:

>>> server = Server('ipa.demo1.freeipa.org', get_info=ALL)>>> conn = Connection(server, auto_bind=True)>>> server.infoDSA info (from DSE):  Supported LDAP Versions: 2, 3  Naming Contexts:    cn=changelog省略>>> server.schemaDSA Schema from: cn=schema  Attribute types:{'ipaNTTrustForestTrustInfo': Attribute type: 2.16.840.1.113730.3.8.11.17  Short name: ipaNTTrustForestTrustInfo  Description: Forest trust information for a trusted domain object省略

登录:

>>> # import class and constants>>> from ldap3 import Server, Connection, ALL, NTLM>>> # define the server and the connection>>> server = Server('10.99.201.86', get_info=ALL)>>> conn = Connection(server, user="Domain\\User", password="password", authentication=“NTLM”)

查看登录信息:

>>> conn.extend.standard.who_am_i()

如果是匿名登录那么返回空。

所以使用用户名密码登录:

>>> conn = Connection(server, 'uid=admin, cn=users, cn=accounts, dc=demo1, dc=freeipa, dc=org', 'Secret123', auto_bind=True)>>> conn.extend.standard.who_am_i()'dn: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org'

使用安全方式登录,两种:LDAP over TLS or the StartTLS extended operation。这里不做详细说明,具体参考官方文档。下面只列出相关代码:

>>> server = Server('ipa.demo1.freeipa.org', use_ssl=True, get_info=ALL)>>> conn = Connection(server, 'uid=admin, cn=users, cn=accounts, dc=demo1, dc=freeipa, dc=org', 'Secret123', auto_bind=True)>>> print(conn)ldaps://ipa.demo1.freeipa.org:636 - ssl - user: uid=admin, cn=users, cn=accounts, dc=demo1, dc=freeipa, dc=org - bound - open - 
- tls not started - listening - SyncStrategy - internal decoder
>>> from ldap3 import Server, Connection, Tls>>> import ssl>>> tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1)>>> server = Server('ipa.demo1.freeipa.org', use_ssl=True, tls=tls_configuration)>>> conn = Connection(server)>>> conn.open()...ldap3.core.exceptions.LDAPSocketOpenError: (LDAPSocketOpenError('socket ssl wrapping error: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)',),)

访问数据

这里介绍同步和异步两种方式。读文档说明,异步是使用独立线程完成操作的。同步的就是发送消息然后等待接受消息。

异步经常用于event-driven,事件驱动。

另外还有一种Compare操作。它既是用来验证一个attribute是否有一个value。这个操作可以用来做密码验证(不用bind操作)。

任何同步操作后Connection对象中都会有一些attribute被populated。

  • result: the result of the last operation (as returned by the server)
  • response: the entries found (if the last operation is a Search)
  • entries: the entries found exposed via the abstraction layer (if the last operation is a Search)
  • last_error: the error occurred in the last operation, if any
  • bound: True if the connection is bound to the server
  • listening: True if the socket is listening to the server
  • closed: True if the socket is not open

搜索操作

搜索操作需要三个参数,但是只有两个是必须:

  • search_base: the location in the DIT where the search will start
  • search_filter: what are you searching

 这里的search_filter有特殊的语法。它是基于assertion,而assertion是一个bracketed expression。assertion是true false或者undefined(等同于false)。assertion使用& | ! 组织,可以包含 = <= >= =* ~=等。另外注意,这里没有 < 和 > 符号。

例如:

(&    (|        (givenName=Fred)        (givenName=John)    )    (mail=*@example.org))

代码示例:

搜索所有person,即objectclass=persono。

>>> from ldap3 import Server, Connection, ALL>>> server = Server('ipa.demo1.freeipa.org', get_info=ALL)>>> conn = Connection(server, 'uid=admin, cn=users, cn=accounts, dc=demo1, dc=freeipa, dc=org', 'Secret123', auto_bind=True)>>> conn.search('dc=demo1, dc=freeipa, dc=org', '(objectclass=person)')True>>> conn.entries[DN: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org, DN: uid=manager,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org, DN: uid=employee,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org, DN: uid=helpdesk,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org]

请求获取部分属性。

>>> conn.search('dc=demo1, dc=freeipa, dc=org', '(&(objectclass=person)(uid=admin))', attributes=['sn','krbLastPwdChange', 'objectclass'])True>>> conn.entries[0]DN: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org    krbLastPwdChange: 2015-09-30 04:06:59+00:00    objectclass: top                 person                 posixaccount                 krbprincipalaux                 krbticketpolicyaux                 inetuser                 ipaobject                 ipasshuser                 ipaSshGroupOfPubKeys    sn: Administrator

属性获取方法:

>>> entry = entries[0]>>> entry.krbLastPwdChangekrbLastPwdChange: 2015-09-30 04:06:59+00:00>>> entry.KRBLastPwdCHANGEkrbLastPwdChange: 2015-09-30 04:06:59+00:00>>> entry['krbLastPwdChange']krbLastPwdChange: 2015-09-30 04:06:59+00:00>>> entry['KRB LAST PWD CHANGE']krbLastPwdChange: 2015-09-30 04:06:59+00:00>>> entry.krbLastPwdChange.values[datetime.datetime(2015, 9, 30, 4, 6, 59, tzinfo=OffsetTzInfo(offset=0, name='UTC'))]>>> entry.krbLastPwdChange.raw_values[b'20150930040659Z']

一点要注意,那就是search_scope表示了搜索范围,有三种。默认SUBTREE。

1 BASE A base search limits the search to the base object 一般用来检查object的存在与否。
2 LEVEL A one-level search is restricted to the immediate children of a base object, but excludes the base object itself. 
3 SUBTREE A subtree search (or a deep search) includes all child objects as well as the base object. 

 

关于LDIF:

>>> print(conn.entries[0].entry_to_ldif())version: 1dn: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=orgobjectclass: topobjectclass: personobjectclass: posixaccountobjectclass: krbprincipalauxobjectclass: krbticketpolicyauxobjectclass: inetuserobjectclass: ipaobjectobjectclass: ipasshuserobjectclass: ipaSshGroupOfPubKeyskrbLastPwdChange: 20150930040659Zsn: Administrator# total number of entries: 1

或者使用JSON表示:

>>> print(entry.entry_to_json()){    "attributes": {        "krbLastPwdChange": [            "2015-09-30 04:06:59+00:00"        ],        "objectclass": [            "top",            "person",            "posixaccount",            "krbprincipalaux",            "krbticketpolicyaux",            "inetuser",            "ipaobject",            "ipasshuser",            "ipaSshGroupOfPubKeys"        ],        "sn": [            "Administrator"        ]    },    "dn": "uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org"

另外关于binary values:

>>> from ldap3.utils.conv import escape_bytes>>> unique_id = b'\xca@\xf2k\x1d\x86\xcaL\xb7\xa2\xca@\xf2k\x1d\x86'>>> search_filter = '(nsUniqueID=' + escape_bytes(unique_id) + ')'>>> conn.search('dc=demo1, dc=freeipa, dc=org', search_filter, attributes=['nsUniqueId'])

关于connection context manager:

>>> with Connection(server, 'uid=admin, cn=users, cn=accounts, dc=demo1, dc=freeipa, dc=org', 'Secret123') as conn:        conn.search('dc=demo1, dc=freeipa, dc=org', '(&(objectclass=person)(uid=admin))', attributes=['sn','krbLastPwdChange', 'objectclass'])        entry = conn.entries[0]True>>> conn.boundFalse>>> entryDN: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=orgkrbLastPwdChange: 2015-09-30 04:06:59+00:00objectclass: top             person             posixaccount             krbprincipalaux             krbticketpolicyaux             inetuser             ipaobject             ipasshuser             ipaSshGroupOfPubKeyssn: Administrator

增加操作

>>> # Create a container for our new entries>>> conn.add('ou=ldap3-tutorial, dc=demo1, dc=freeipa, dc=org', 'organizationalUnit')>>> True>>> # Add some users>>> conn.add('cn=b.young,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'inetorgperson', {'givenName': 'Beatrix', 'sn': 'Young', 'departmentNumber':'DEV', 'telephoneNumber': 1111})>>> True>>> conn.add('cn=j.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'inetorgperson', {'givenName': 'John', 'sn': 'Smith', 'departmentNumber':'DEV',  'telephoneNumber': 2222})>>> True>>> conn.add('cn=m.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'inetorgperson', {'givenName': 'Marianne', 'sn': 'Smith', 'departmentNumber':'QA',  'telephoneNumber': 3333})>>> True>>> conn.add('cn=quentin.cat,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org', 'inetorgperson', {'givenName': 'Quentin', 'sn': 'Cat', 'departmentNumber':'CC',  'telephoneNumber': 4444})

 

转载于:https://my.oschina.net/shawnplaying/blog/733338

你可能感兴趣的文章
Mac下安装nginx
查看>>
<转>IOS多线程
查看>>
微信服务号、订阅号和企业号的差别(运营和开发两个角度)
查看>>
AOP之AspectJ
查看>>
SHELL里执行HIVE导出文件处理成CSV文件
查看>>
Python菜鸟之路:Django 路由补充1:FBV和CBV - 补充2:url默认参数
查看>>
【转】生活感悟
查看>>
【leetcode】965. Univalued Binary Tree
查看>>
第十五周学习报告
查看>>
tomcat用户设置
查看>>
LINQ实现行列转换
查看>>
PHP最全笔记(五)(值得收藏,不时翻看一下)
查看>>
拦截器与过滤器的区别
查看>>
移动开发知识点收集
查看>>
黑客教父详解账号泄露全过程:1亿用户已泄露
查看>>
smarty练习: 设置试题及打印试卷
查看>>
替换子节点时 的注意事项
查看>>
groupinfo
查看>>
vmware产品
查看>>
Django2.1.3 smtp 邮件 553报警
查看>>