原本想使用python-ldap,可是安装过程当中出现以下问题,并且花费不少时间精力没有解决该问题,因此放弃python-ldap,而使用ldap3。html
E:\>pip install python-ldap Collecting python-ldap D:\app\Python27\lib\site-packages\pip\_vendor\requests\packages\urllib3\util\ssl_.py:318: SNIMissingWarning: An HTTPS re quest has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newe r version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#sni missingwarning. SNIMissingWarning 省略。。。 C:\Users\zhaoxp2\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe /c /nologo /O x /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\Pyth on27\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 ope n 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\\tem p\\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很简单,pip install ldap3便可。地址:https://pypi.python.org/pypi/ldap3python
为何使用ldap3?下面是给出的缘由:express
A strictly RFC 4510 conforming LDAP V3 pure Python client library. The same codebase works with Python 2, Python 3, PyPy, PyPy3 and Nuikta.json
接下来是ldap3的使用介绍。安全
参考:http://ldap3.readthedocs.io/tutorial.html服务器
1 anonymousapp
2 simple passworddom
3 SASL(simple authentication and security layer)ssh
这里注意文档中说明了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 server print conn
获取服务器信息:
>>> server = Server('ipa.demo1.freeipa.org', get_info=ALL) >>> conn = Connection(server, auto_bind=True) >>> server.info DSA info (from DSE): Supported LDAP Versions: 2, 3 Naming Contexts: cn=changelog 省略 >>> server.schema DSA 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 - <local: 192.168.1.101:51438 - remote: 209.132.178.99:636> - 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 anybound
: True if the connection is bound to the serverlistening
: True if the socket is listening to the serverclosed
: True if the socket is not open搜索操做须要三个参数,可是只有两个是必须:
search_base
: the location in the DIT where the search will startsearch_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.krbLastPwdChange krbLastPwdChange: 2015-09-30 04:06:59+00:00 >>> entry.KRBLastPwdCHANGE krbLastPwdChange: 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: 1 dn: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org objectclass: top objectclass: person objectclass: posixaccount objectclass: krbprincipalaux objectclass: krbticketpolicyaux objectclass: inetuser objectclass: ipaobject objectclass: ipasshuser objectclass: ipaSshGroupOfPubKeys krbLastPwdChange: 20150930040659Z sn: 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.bound False >>> entry 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
>>> # 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})