Python 和 LDAP

用 Python-LDAP 执行 LDAP 的 CRUD(创建、读取、更新和删除)操作:

大多数系统管理员在职业生涯中都会遇到需要与 LDAP 服务器交互的情况。本文讲解如何使用 LDAP 实现 Apache 身份验证,以及如何使用 Python 模块 python-ldap 在 OpenLDAP 数据库上执行 CRUD(即创建、读取、更新和删除)操作。

Noah Gift, 软件工程师, Giftcs

http://www.ibm.com/developerworks/i/p-ngift.jpgNoah Gift 是 O'Reilly 出版的“Python For Unix and Linux”一书的合著者。他是一名作家、演说家、顾问和社区负责人,并为 IBM developerWorks、Red Hat Magazine、O'Reilly 和 MacTech 众多出版商撰稿。他的咨询公司的网站是 www.giftc.com,他的个人网站是 www.noahgift.com。Noah 目前还是 www.pyatl.org 网站的组织者,该网站是佐治亚州亚特兰大的 Python 用户组。他拥有加州洛杉矶的 CIS 的硕士学位,加州 Poly San Luis Obispo 的营养科学学士学位,他还是通过 Apple 和 LPI 认证的系统管理员,他曾经在许多公司工作过,如加利福尼亚理工学院、Disney Feature Animation、Sony Imageworks 和 Turner Studios。在空闲的时候,他喜欢和妻子 Leah,以及他们的儿子 Liam 一起度过,弹奏钢琴以及进行宗教活动。


developerWorks 投稿作者

Grig Gheorghiu, 技术主管, RIS Technology

http://www.ibm.com/developerworks/i/p-ggheorghui.jpgGrig Gheorghiu 是 RIS Technology 的技术主管,这是位于洛杉矶的一家 Web 主机托管公司。Grig 有 15 年行业经验,担任过程序员、研究实验室经理、系统/网络/安全架构师、IT 顾问和测试工程师。Grig 是 Python 和敏捷测试社区的活跃成员。

他维护的博客 http://agiletesting.blogspot.com 专门讨论敏捷测试、Python 编程以及自动化测试工具和技术。Grig 是 Southern California Python Interest Group(也称为 'the SoCal Piggies')的创始人。他与妻子和两个孩子生活在洛杉矶。



2009 年 1 月 05 日

简介

在本文中,作者将讲解如何在 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 以及它的用途是什么?

那么,究竟什么是 LDAP 呢?按照严格的定义,LDAP 代表 Lightweight Directory Access Protocol。但是,这个名称已经成了一种目录体系结构的同义词。当人们提到 LDAP 时,常常不是指协议,而是指目录服务。

LDAP 的最新版本是 V3。LDAP 被设计成一种通用目录,但是有几个约定。一个记录由一个 DN 和一个或多个属性组成,属性是在属性定义中定义的。LDAP 数据库的模式与典型的关系数据库模式定义语言很不一样。例如,典型的关系数据库是基于表的,而 LDAP 结合了继承。如果希望进一步了解 LDAP 理论,我们强烈建议您阅读 参考资料 中列出的 OpenLDAP 图书。

那么,LDAP 的用途是什么?它用于对构建 IT 基础结构的人进行身份验证。它还能够与 Samba 很好地配合,所以有经验的系统管理员可以以零成本设置非常高级的 IT 基础结构,而不必承担其他专有目录解决方案的成本。“身份验证” 意味着,基础结构中的所有计算机只需与 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。


设置 Apache 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 执行 CRUD 操作

现在,准备使用 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 帮助审阅了本文!


下载

描述名字大小
示例 LDAP 脚本和测试数据ldap_crud_code.zip4KB

参考资料

学习

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Open source
ArticleID=362073
ArticleTitle=Python 和 LDAP
publish-date=01052009