Python 和 LDAP
用 Python-LDAP 执行 LDAP 的 CRUD(创建、读取、更新和删除)操作:
在本文中,作者将讲解如何在 Amazon EC2 虚拟机上安装 OpenLDAP 的实例,设置 Apache/LDAP 身份验证,然后使用 Python 执行 CRUD(即创建、读取、更新和删除)操作。一定要注意,可以在 Fedora、Ubuntu、Red Hat、AIX® 等操作系统上安装 LDAP。但是在本文中,我们决定采用 Amazon EC2 虚拟机。您可以在任何 Linux® 发行版或手边的任何环境中进行实践。最后,我们将在本文中讨论许多代码和复杂的技术。您应该首先下载 示例代码,供阅读本文时参考。
通过程序控制 LDAP 常常是系统管理员的工作,所以 Python 中存在一个用来操作 LDAP 的库就不奇怪了。python-ldap 模块已经存在了一段时间了,在 参考资料 一节中可以找到官方文档的链接。
我们假设您熟悉一般的 LDAP 概念,比如目录模式、Distinguished Names (DN)、Common Names (CN)、过滤器和属性。本文不是 LDAP 教程;我们不喜欢大谈理论,而是主要关注使用和管理 LDAP 数据库的实际示例。
如果希望按照本文的说明设置 LDAP,那么需要一个 Fedora Core 8 实例。我们使用一个运行 Fedora Core 8 32-bit 的 Amazon EC2 虚拟机实例。可以在物理服务器上安装 LDAP,也可以使用您选择的技术在虚拟机上安装。注意,对于所有示例,我们使用一个称为 unisonis.com 的域,但是有一份 RFC 建议使用 example.com。
步骤 1:使用 yum 安装 openldap 包:
[root@domU ]# yum install openldap openldap-devel openldap-servers openldap-clients [root@domU ]# yum list installed | grep openldap openldap.i386 2.3.39-4.fc8 installed openldap-clients.i386 2.3.39-4.fc8 installed openldap-devel.i386 2.3.39-4.fc8 installed openldap-servers.i386 2.3.39-4.fc8 installed |
步骤 2:设置管理员密码(我们将把 SSHA 散列值粘贴在 slapd.conf 中)。注意,slapd 代表 Standalone LDAP 服务,所以此服务控制 LDAP 本身:
[root@domU ]# slappasswd New password: Re-enter new password: |
步骤 3:编辑 slapd.conf 配置文件并添加一般 LDAP 安装所需的条目,比如根 DN 和根/管理员密码:
[root@domU ]# vi /etc/openldap/slapd.conf #Add entries: database bdb suffix "dc=unisonis,dc=com" rootdn "cn=Manager,dc=unisonis,dc=com" rootpw {SSHA}pasted_from_slappasswd_output directory /var/lib/ldap |
步骤 4:启动 LDAP 服务:
[root@domU ]# service ldap start Starting slapd: [ OK ] |
步骤 5:运行 LDAP 并搜索 'namingContexts' 属性,从而测试 LDAP:
[root@domU ]# ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts # extended LDIF # # LDAPv3 # base <> with scope baseObject # filter: (objectclass=*) # requesting: namingContexts # # dn: namingContexts: dc=unisonis,dc=com # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1 |
步骤 6:使用 ldapadd 和 LDIF 文件在 LDAP 数据库中添加更多条目。注意,LDIF 代表 LDAP Data Interchange Format,这是用于对 LDAP 数据库进行大量更新的数据格式:
[root@domU ]# cat unisonis.ldif dn: dc=unisonis,dc=com objectclass: dcObject objectclass: organization o: Example Company dc: unisonis dn: cn=Manager,dc=unisonis,dc=com objectclass: organizationalRole cn: Manager [root@domU ]# ldapadd -x -D "cn=Manager,dc=unisonis,dc=com" -W -f unisonis.ldif Enter LDAP Password: adding new entry "dc=unisonis,dc=com" adding new entry "cn=Manager,dc=unisonis,dc=com" |
步骤 7:下一步是用示例条目填充 LDAP 目录。我们使用三个小丑的信息(灵感来源于 LDAP 文章 http://www.yolinux.com/TUTORIALS/LinuxTutorialLDAP.html):
[root@domU ]# cat stooges.ldif; # to conserve space, we show the LDAP data for only one of the three stooges dn: ou=MemberGroupA,dc=unisonis,dc=com ou: MemberGroupA objectClass: top objectClass: organizationalUnit description: Members of MemberGroupA dn: ou=MemberGroupB,dc=unisonis,dc=com ou: MemberGroupB objectClass: top objectClass: organizationalUnit description: Members of MemberGroupB dn: cn=Larry Fine,ou=MemberGroupA,dc=unisonis,dc=com ou: MemberGroupA o: stooges cn: Larry Fine objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson mail: LFine@unisonis.com givenname: Larry sn: Fine uid: larry homePostalAddress: 15 Cherry Ln. Plano TX 78888 postalAddress: 215 Fitzhugh Ave. l: Dallas st: TX postalcode: 75226 telephoneNumber: (800)555-1212 homePhone: 800-555-1313 facsimileTelephoneNumber: 800-555-1414 userPassword: larrysecret title: Account Executive destinationindicator: /bios/images/lfine.jpg [root@domU ]# ldapadd -x -D "cn=Manager,dc=unisonis,dc=com" -W -f stooges.ldif |
如果愿意,现在就可以对刚才创建的 LDAP 数据库做各种搜索。下面的示例搜索与 'stooges' 组织相关的所有 LDAP 条目:
[root@domU conf.d]# ldapsearch -x -b 'dc=unisonis,dc=com' '(o=stooges)' |
在下一节中,我们讨论如何通过配置 Apache 实现 LDAP 身份验证,然后讨论 Python 和 LDAP。
LDAP 最常见的用途之一是为 Web 服务等提供身份验证数据。在本节中,我们使用前面预先填充的 LDAP 数据库控制对一个 Apache 虚拟主机的访问。
首先,需要为使用 LDAP 身份验证的虚拟主机创建一个 Apache 配置文件。对于试图登录的用户,要求提供有效的电子邮件地址和密码。配置文件如下所示:
[root@domU ]# cat /etc/httpd/conf.d/unisonis.conf <VirtualHost *:80> ServerName www.unisonis.com DocumentRoot "/ebs1/www/unisonis" <Directory "/ebs1/www/unisonis"> AuthType Basic AuthName "unisonis.com: please login with email address" AuthBasicProvider ldap AuthLDAPURL ldap://localhost:389/dc=unisonis,dc=com?mail?sub?(o=stooges) require valid-user Order Allow,Deny Allow from all Options Indexes FollowSymLinks AllowOverride None </Directory> </VirtualHost> |
通过 mod_auth_ldap 模块向 Apache 提供 LDAP 身份验证,此模块是在 Fedora Core 8 httpd 包中默认安装的。对于试图登录的用户,要求在 'stooges' 组织中包含有效的电子邮件地址和密码,这样才能访问上面定义的 Apache 虚拟主机。请注意 AuthLDAPURL 指令,它指定通过 LDAP 服务器实现用户身份验证所用的查询。我们搜索 'mail' 属性并应用过滤器 (o=stooges)。AuthLDAPURL 指令的完整语法请参见 http://httpd.apache.org/docs/2.0/mod/mod_auth_ldap.html#authldapurl。
关于在 Apache 上配置 LDAP 的更多信息请参见 参考资料。
现在,准备使用 Python 与 LDAP 交互。为此,必须安装 python-ldap 模块。在 参考资料 中,可以找到关于安装此模块的详细信息的链接。实际上,只需要执行 “easy install”。首先下载 easy_install 脚本:
http://peak.telecommunity.com/dist/ez_setup.py |
然后输入:
sudo easy_install python-ldap |
注意,根据操作系统的不同,此包依赖的一些软件略有差异。如果在安装此包时遇到问题,请仔细阅读安装说明。
安装 python-ldap 之后,就可以执行 CRUD 操作了。下面编写一个执行这些操作的类。
Python LDAP CRUD 类
#!/bin/env python import sys, ldap LDAP_HOST = 'localhost' LDAP_BASE_DN = 'dc=unisonis,dc=com' MGR_CRED = 'cn=Manager,dc=unisonis,dc=com' MGR_PASSWD = 'mypasswd' STOOGE_FILTER = 'o=stooges' class StoogeLDAPMgmt: def __init__(self, ldap_host=None, ldap_base_dn=None, mgr_cred=None, mgr_passwd=None): if not ldap_host: ldap_host = LDAP_HOST if not ldap_base_dn: ldap_base_dn = LDAP_BASE_DN if not mgr_cred: mgr_cred = MGR_CRED if not mgr_passwd: mgr_passwd = MGR_PASSWD self.ldapconn = ldap.open(ldap_host) self.ldapconn.simple_bind(mgr_cred, mgr_passwd) self.ldap_base_dn = ldap_base_dn def list_stooges(self, stooge_filter=None, attrib=None): if not stooge_filter: stooge_filter = STOOGE_FILTER s = self.ldapconn.search_s(self.ldap_base_dn, ldap.SCOPE_SUBTREE, stooge_filter, attrib) print "Here is the complete list of stooges:" stooge_list = [] for stooge in s: attrib_dict = stooge[1] for a in attrib: out = "%s: %s" % (a, attrib_dict[a]) print out stooge_list.append(out) return stooge_list def add_stooge(self, stooge_name, stooge_ou, stooge_info): stooge_dn = 'cn=%s,ou=%s,%s' % (stooge_name, stooge_ou, self.ldap_base_dn) stooge_attrib = [(k, v) for (k, v) in stooge_info.items()] print "Adding stooge %s with ou=%s" % (stooge_name, stooge_ou) self.ldapconn.add_s(stooge_dn, stooge_attrib) def modify_stooge(self, stooge_name, stooge_ou, stooge_attrib): stooge_dn = 'cn=%s,ou=%s,%s' % (stooge_name, stooge_ou, self.ldap_base_dn) print "Modifying stooge %s with ou=%s" % (stooge_name, stooge_ou) self.ldapconn.modify_s(stooge_dn, stooge_attrib) def delete_stooge(self, stooge_name, stooge_ou): stooge_dn = 'cn=%s,ou=%s,%s' % (stooge_name, stooge_ou, self.ldap_base_dn) print "Deleting stooge %s with ou=%s" % (stooge_name, stooge_ou) self.ldapconn.delete_s(stooge_dn) |
此类中的方法名就能够说明方法的作用,所以我们只讨论在实现这个类时的一些步骤。如果您已经按照前面的步骤填充了 LDAP 数据库,现在还应该下载代码示例。
首先,创建此类的实例:
l = StoogeLDAPMgmt() |
然后就可以执行 CRUD 了。
接下来,用 Python 代码添加一些功能。应该复制并粘贴下载的源代码,因为手工输入容易出错。下面是 CRUD 中的 “C”(创建):
LDAP 创建
# add new stooge: Harry Potter stooge_name = 'Harry Potter' stooge_ou = 'MemberGroupB' stooge_info = {'cn': ['Harry Potter'], 'objectClass': ['top', 'person', 'organizationalPerson', 'inetOrgPerson'], 'uid': ['harry'], 'title': ['QA Engineer'], 'facsimileTelephoneNumber': ['800-555-3318'], 'userPassword': ['harrysecret'], 'postalCode': ['75206'], 'mail': ['HPotter@unisonis.com'], 'postalAddress': ['2908 Greenville Ave.'], 'homePostalAddress': ['14 Cherry Ln. Plano TX 78888'], 'pager': ['800-555-1319'], 'homePhone': ['800-555-7777'], 'telephoneNumber': ['(800)555-1214'], 'givenName': ['Harry'], 'mobile': ['800-555-1318'], 'l': ['Dallas'], 'o': ['stooges'], 'st': ['TX'], 'sn': ['Potter'], 'ou': ['MemberGroupB'], 'destinationIndicator': ['/bios/images/hpotter.jpg'], } try: l.add_stooge(stooge_name, stooge_ou, stooge_info) except ldap.LDAPError, error: print 'problem with ldap',error |
现在对此条目执行 “R”(读取):
LDAP 读取
# see if it was added l.list_stooges(attrib=['cn', 'mail', 'homePhone']) |
现在更新此条目,即执行 “U”:
LDAP 更新
# now modify home phone stooge_modified_attrib = [(ldap.MOD_REPLACE, 'homePhone', '800-555-8888')] try: l.modify_stooge(stooge_name, stooge_ou, stooge_modified_attrib) except ldap.LDAPError, error: print 'problem with ldap',error |
最后,执行 “D”(删除):
LDAP 删除
# now delete Harry Potter try: l.delete_stooge(stooge_name, stooge_ou) except ldap.LDAPError, error: print 'problem with ldap',error |
本文简要介绍了在 Amazon EC2 Fedora 实例上安装 OpenLDAP 的过程,我们用测试数据填充了 LDAP 数据库,简要讨论了从命令行与 LDAP 数据库交互,演示了如何通过配置 Apache 实现 LDAP 身份验证。最后,通过 Python 代码控制 LDAP。Python 示例最出色的优点之一是,与脚本(比如 bash 脚本)代码相比,Python 代码看起来舒服得多。Python 代码因其简洁和可读性强而闻名,这在处理 LDAP 编程等复杂操作时表现尤为突出。
我们实际上没有研究 LDAP 和 Python 的实际示例,但是介绍了如何使用 API 执行 CRUD 操作。使用 python-ldap 库是一种很实用的想法。在每次创建 TRAC 新实例时,您可能希望用 LDAP 组中的所有用户填充不同的 TRAC 项目管理网站。这很容易用我们讨论的技术实现:查询 LDAP 组,在 TRAC 中为每个组成员插入许可。还有许多其他操作 LDAP 的实用方法,希望本文能帮助您如何在自己的项目中进一步利用 LDAP。
我衷心感谢 David Goodger 帮助审阅了本文!