前言
本 来,我应该准备一篇精彩的演说辞,从LDAP应用的方方面面讲起,细数LDAP在各种场合应用的成功案例,大肆渲染LDAP应用的辉煌前景,指出有多少机 构和组织的关键业务是建立在LDAP的基础上的,同时从面向对象的角度分析LDAP所具有的无可比拟的优势。但是,我没有。因为,一是,这个东西已经提上 日程很多时候了,而我依然没有能够给出一个完整的解决方案,甚至也还没有深入到LDAP的API,所以实在不好意思再准备这样一篇演说辞,尽管原因是多方 面的,但是主要的原因还是因为我琐事缠身,人在江湖,身不由己之故;二是,如果仅仅从最基本的应用而言,LDAP协议本身并不是非常的难,但是,要深入理 解LDAP的核心思路,谈何容易,君不见,RFC文档,动辄洋洋洒洒数万言;三是,在基础数据库领域,中国已无法与国外的技术抗衡,而新兴的LDAP目录 协议无疑是一个可以让中国在这方面有所建树的话题,只是,我所学并非计算机专业,所好也并非在各种协议的解读中穷尽一生心血,所谓报国无门者,大概如此。 上古贤能葛优曾说过,知我者谓我心忧,不知我者谓我何求,此言不假。
还是从最基本的说起吧,我的计划是,先给出一个最基本的框架,在此基础上,构 建基本的目录应用;然后将重点放在schema的构建和组织上,说明如何定制自己的schema;最后,我准备以LDAP的API结束LDAP目录服务的 讨论。我将会主要关注的对象是,开源社区中的著名项目,OpenLDAP。又因为已经是奔三的人了,各种逆天的课程已经在百般无耻下流地冲着我狞笑了,所 以我可能没有足够的时间把这些琐碎的学习笔记或者所谓的学习OpenLDAP的心路历程贴上来,但是,我会尽量保证每个星期有一篇blog保质保量的准时 出现在dorm blog上;同时因为水平有限,疏漏之处,在所难免,恳请各位兄弟斧正。
安装
在*nix下安装OpenLDAP的方式有以下几种:
1。如果你使用的是Red Hat,那么可以通过RPM包安装;
2。如果你使用的是debian,那么可以通过apt-get安装;
3。如果你使用的是BSD家族的FreeBSD或者OpenBSD,那么我推荐你采用ports方式安装;
4。如果你想尝试最新的版本,最前沿的功能,你可能需要按照传统的./configure && make && make install三步曲方式安装;
说 明:不推荐第四种安装方式,因为OpenLDAP不论是其前端对网络层的处理还是后端对数据库的操作,都涉及到*nix下共享库的问题,很容易在make 的过程中出现error,给学习和使用OpenLDAP的幼小心灵造成严重的心理创伤。前三种方法很安全,因为所有相关软件的依赖关系已经由一些乐于奉献 的人给你构建好了,不会出错。安装完成之后,在/usr目录下会出现下面这些可用的东西(注:具体的布局会因系统而异,比如FreeBSD就会将所有东西 都放入/usr/local下,而debian则不然):
服务器:slapd,slurpd
服务端工具:slapadd,slapcat,slapindex,slappassword
服务端配置文件:slapd.conf,slapd.access
客户端工具:ldapadd,ldapdelete,ldapmodify,ldapsearch,ldapmodrdn
客户端配置文件:ldap.conf
如果使用服务端工具操作后端数据库,必须首先把slapd服务进程停下来;而如果采用客户端工具如ldapadd等则不需要先kill掉slapd,因此推荐使用ldap客户端工具进行涉及数据库添加,删除,更新等的操作。

OpenLDAP系列之二

这一节我们主要关注如何配置基本的LDAP服务以及构建简单的地址簿.

1.OpenLDAP的服务器程序是/usr/local/libexec/slapd,相应的配置文件为 /usr/local/etc/openldap/slapd.conf,这是OpenLDAP最为重要的一个配置文件,它控制着slapd的各个方面. 它和apache服务器的httpd.conf配置文件所起的作用是一样的.slapd.conf的配置选项很多,完整的配置说明请参见man slapd.conf,通常情况下,OpenLDAP安装之后在/usr/local/etc/openldap目录下包含了slapd.conf的一个 配置范例,如果不是需要对OpenLDAP的各个方面进行精确控制,那么里面的选项多数情况下对我们来说就已经足够了.以下是我的配置文件,所用的系统是 FreeBSD 5.3,其他系统与此类似.
#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
include         /usr/local/etc/openldap/schema/core.schema
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema

# Define global ACLs to disable default read access.

pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args

#######################################################################
# BDB database definitions
#######################################################################

database        bdb
suffix          "o=dormforce.net,c=cn"
rootdn          "cn=root,o=dormforce.net,c=cn"
# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw          {MD5}cZZpqIYIUsmEXc/SuEslTA==
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory       /var/db/openldap-data
# Indices to maintain
index   objectClass     eq

下面我对这个简单的配置文件进行解释:
首先,以#开头的行都是注释;如果某行的下一行是以空格或者制表键空出前面的几格,并且没有以#注释掉,那么这一行被认为是上一行的延续. slapd.conf文件一般分为三个部分,最前面是global options,其次是backend options,之后是database options;global options指定全局范围的options;backend options指定某种后端数据库的options,比如可以对DB后端数据库和LDBM后端数据库分别指定一些选项;database options对某个具体的数据库指定一些选项.backend options可以覆盖global options,database options可以覆盖backend options;一开始是三个include行,是global options的一部分:

include         /usr/local/etc/openldap/schema/core.schema
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema

这三行指定了包含的schema文件,需要知道,schema文件对于LDAP服务器而言至关重要.在LDAP服务器上存储的各种数据都可以看做是某种对 象,而schema文件指定了每个对象的属性,某些属性是必须要有的,有些属性则是可选的.现在,我们只需要知道,在本文件中,我们必需要包含的就是这三 个schema文件.这三个文件限定了我们将要在服务器上存储的数据对象.

pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args

这两行指定将slapd的进程ID号保存到/var/run/openldap/slapd.pid文件中,把slapd的参数保存到 /var/run/openldap/slapd.args文件中.我们没有指定访问控制列表ACL,因此,global options到这里结束.默认情况下,slapd服务器进程允许匿名访问,匿名访问的权限被设置为只读.

本配置文件中我们省略了backend options,因为我们只使用到了BDB数据库;接下来,我们看一下BDB数据库的配置选项:
#######################################################################
# BDB database definitions
#######################################################################

database        bdb
suffix          "o=dormforce.net,c=cn"
rootdn          "cn=root,o=dormforce.net,c=cn"
# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw          {MD5}cZZpqIYIUsmEXc/SuEslTA==
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory       /var/db/openldap-data
# Indices to maintain
index   objectClass     eq

这里:

database        bdb

这一行指定数据库为BDB数据库;

suffix          "o=dormforce.net,c=cn"

指定BDB数据库保存目录树中的"o=dormforce.net,c=cn"子树;

rootdn          "cn=root,o=dormforce.net,c=cn"

相当于指定了管理此目录子树的root帐号;

rootpw          {MD5}cZZpqIYIUsmEXc/SuEslTA==

相当于指定了管理此目录子树的root用户的口令,本例中我使用了MD5进行加密;

directory       /var/db/openldap-data

指定了BDB数据库所在的文件系统目录;

index   objectClass     eq

指定了数据库索引(注:这个我也没弄明白,但是知道它是用来提高数据库访问速度的).

整个slapd.conf配置文件到此结束.下面我对
suffix          "o=dormforce.net,c=cn"
这一行做一下解释:
这里的o表示组织名,我们的组织名是dormforce.net,而c表示国家名,中国表示为cn.
关于目录中识别名DN的命名,RFC并没有规定标准的方法,但是通常使用的有三种方法:
(1)传统方式,顶层为国家名,次顶层为组织名,如:
suffix        "o=dormforce,c=cn
(2)域名方式,按照DNS命名方式命名,如:
suffix        "dc=dormforce,dc=net"
其中,dc意为domain component.
(3)混合方式,以上两种方式的结合体,本例中就是这样命名的:
suffix          "o=dormforce.net,c=cn"

2.配置文件修改好之后,用下面的办法启动slapd服务器进程:
首先,修改/etc/rc.conf文件,添加下面三行:
slapd_enable="YES"
slapd_flags='-h "ldapi://%2fvar%2frun%2fopenldap%2fldapi/ ldap://0.0.0.0/"'
slapd_sockets="/var/run/openldap/ldapi"
然后,在控制台下输入如下命令:
/usr/local/etc/rc.d/slapd.sh start
默认情况下,OpenLDAP会附带安装几个客户端工具,因此我们现在就可以用它们来检查slapd是否正常启动了,在控制台下输入:
ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts
如果能看到类似下面的输出,则表明服务器进程已经正常运行了:
# extended LDIF
#
# LDAPv3
# base <> with scope base
# filter: (objectclass=*)
# requesting: namingContexts
#

#
dn:
namingContexts: o=dormforce.net,c=cn

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

3.下面,我们给LDAP目录服务器添加一些数据,首先打开任何一个编辑器输入如下文本:
#THIS LDIF CAN ONLY BE CREATED AND MODIFIED BY ROCKINS
dn: o=dormforce.net,c=cn
o: dormforce.net
objectClass: organization

dn: ou=cert,o=dormforce.net,c=cn
ou: cert
objectClass: organizationalUnit

dn: ou=noc,o=dormforce.net,c=cn
ou: noc
objectClass: organizationalUnit

dn: ou=synx,o=dormforce.net,c=cn
ou: synx
objectClass: organizationalUnit

dn: ou=member,o=dormforce.net,c=cn
ou: member
objectClass: organizationalUnit

dn: cn=ChenYunChuan,ou=member,o=dormforce.net,c=cn
cn: ChenYunChuan
cn: Rockins
sn: 006
carLicense: A006
departmentNumber: 1
displayName: ChenYunChuan
employeeNumber: 006
employeeType: worker
givenName: Chen
homePhone: 028-82851445
mail: ybc2084@163.com
mobile: 123456789
roomNumber: RunXin 1#330
jpegPhoto:< file:///tmp/hero.jpeg
objectClass: organizationalPerson
objectClass: inetOrgPerson

然后,输入如下命令:
cyc# ldapadd -x -D "cn=root,o=dormforce.net,c=cn" -W -f depart.ldif
将会提示输入LDAP管理口令:
Enter LDAP Password:
输入口令之后,如果出现:
adding new entry "o=dormforce.net,c=cn"

adding new entry "ou=cert,o=dormforce.net,c=cn"

adding new entry "ou=noc,o=dormforce.net,c=cn"

adding new entry "ou=synx,o=dormforce.net,c=cn"

adding new entry "ou=member,o=dormforce.net,c=cn"

adding new entry "cn=ChenYunChuan,ou=member,o=dormforce.net,c=cn"
则表明数据添加成功.
本例中,我们先添加了一个组织o=dormforce.net,c=cn;然后添加了四个ou(组织单元)cert,noc,synx,member,这 里的member组织单元是专门用来存放用户数据的;最后在member组织单元中添加了一个用户cn=ChenYunChuan(我的名字).这个用户 有各种各样的属性,如:
homePhone: 028-82851445
mail: ybc2084@163.com
mobile: 123456789
roomNumber: RunXin 1#330
jpegPhoto:< file:///tmp/hero.jpeg
如果你使用的是专门的LDAP客户端工具,你也可以看到保存到LDAP目录中的jpegPhoto.

4.下面,我们就可以通过邮件客户端,比如Outlook Express搜索我们刚刚创建的LDAP目录了,有关Outlook Express访问LDAP目录服务的具体情况,不在本文讨论的范围之内,详情请参见Outlook Express自带的说明文档.

OpenLDAP系列之三

(注:本文主要关注对OpenLDAP的方案进行扩展,需要对OpenLDAP有一定的了解和背景知识,有关OpenLDAP的相关情况,请参见[url]http://www.openldap.org[/url]
本文由<<OpenLDAP管理员指南>>第八章翻译而来,原文请见
[url]http://www.openldap.org/doc/admin22/schema.html[/url]
因为是翻译初稿,未经详细推敲和验证,因此按照文中实例进行操作时请三思.同时由于少数几个地方理解尚不透彻,语句可能不甚通顺,我将会在全书翻译完成之后进行进一步的修正,敬请期待.另,版权所有,侵权必究.)

8. 方案规范(Schema Specification)

本章描述了如何扩展slapd的方案(schema).本章假设读者已经熟悉了LDAP/X.500信息模型.

在本章的第一节--已发布的方案文件(Distributed Schema Files)一节中详细描述了发布包中已经定义了的可选的方案,以及指明了从哪里可以获得其它的方案定义.在本章的第二节,方案扩展(Extending Schema),详细描绘了如何定义新的方案项目(schema items).

本章将不讨论如何对slapd内置的系统方案进行扩展,因为这需要在源代码级进行修改.系统方案包含了所有可操作的属性类型(attribute type)或者对像类(object class),这些对像类可能要求必需有或者可以有某个可操作的属性(直接或者间接需要,译者注:如果从上一级对像类或者属性类型继承则属于间接需要).

8.1. 已发布的方案文件(Distributed Schema Files)

OpenLDAP的发布版中包含有一整套为方便你使用而预先定义好的方案规范(Schema Specification).每一组定义的集合都放在某一个方案文件中,而这些方案文件是可以包含到你的slapd.conf(5)配置文件中的(使用 include指令).所有这些方案文件通常都安装在/usr/local/etc/openldap/schema目录下.

Table 8.1: Provided Schema Specifications File     Description
core.schema             OpenLDAP core (required)
cosine.schema         Cosine and Internet X.500 (useful)
inetorgperson.schema     InetOrgPerson (useful)
misc.schema             Assorted (experimental)
nis.schema             Network Information Services (FYI)
openldap.schema         OpenLDAP Project (experimental)

要使用这些方案文件,你只需要在slapd.conf(5)的全局定义部分包含所需要的文件就可以了.比如像下面这样:

# include schema
        include /usr/local/etc/openldap/schema/core.schema
        include /usr/local/etc/openldap/schema/cosine.schema
        include /usr/local/etc/openldap/schema/inetorgperson.schema

还有一些其它的文件也是可用的.请参考OpenLDAP FAQ ([url]http://www.openldap.org/faq/[/url]).

------------------------------------------------------------------------------------
注意:你不应该修改这些文件中定义的任何方案项目(schema items).
------------------------------------------------------------------------------------

8.2. 方案扩展(Extending Schema)

可以扩展slapd(8)用到的方案以支持额外的语法,匹配规则,属性类型,和对像类.本章详细描述了用户如何通过slapd当前支持的语法和匹配规则来 给自己的应用加入定制的属性类型和对像类,另外也可以扩展slapd来支持额外的语法,匹配规则和系统方案,但是这需要一定的编程知识,因而不在本文的讨 论范围之内.

定义一个新的方案有五个步骤:

1.获取对像OID
    2.选择一个名称前缀
    3.创建本地方案文件
    4.定义需要定制的属性类型(如果需要的话)
    5.定义需要定制的对像类

8.2.1. 对像标识符(Object Identifiers)

每个方案元素都是通过一个全局唯一的对像标识符(OID)来进行标识的.OID也被用于标识其它的对像.它们往往会出现在一些通过ASN.1进行描述的协 议中.特别是,它们在简单网络管理协议(SNMP)中有着很重要的应用.因为OID是以层次结构进行组织的,因此你所在的组织可以获得一个OID并且根据 需要在此基础上衍生出所需要的分支.比如,如果你的组织被授予的OID为1.1,那么你可以像下面这样衍生出一些分支树:

Table 8.2: Example OID hierarchy 
OID         Assignment
1.1         Organization's OID
1.1.1         SNMP Elements
1.1.2         LDAP Elements
1.1.2.1     AttributeTypes
1.1.2.1.1     myAttribute
1.1.2.2     ObjectClasses
1.1.2.2.1     myObjectClass

当然,你可以在你所在组织的OID下面设计符合组织具体要求的层次结构.无论你选择了什么样的层次结构,你都应该对你所指定的OID进行记录.这可能只是 一个简单的文本文件,也可能是某种更为复杂的组织方式,比如OpenLDAP OID Registry ([url]http://www.openldap.org/faq/index.cgi?file=197[/url]).

要了解更多关于对像标识符的信息(以及所列出的相关服务)请参见:
[url]http://www.alvestrand.no/harald/objectid/.[/url]

无论在什么情况下,你都不应该强行占用(hijack)一个OID的名称空间!

要想无偿地得到一个已注册的OID,请在Internet Assigned Numbers Authority (IANA)所维护的Private Enterprise arc下申请一个OID.任何私营企业(组织)都可以请求获得一个该arc下的OID.只需要到IANA的http: //www.iana.org/cgi-bin/enterprise.pl上填写一份表格即可,你的官方OID通常会在几天后发送给你.你的顶层OID 将会是类似于这样的一串数字:1.3.6.1.4.1.X,这里的X指代的是一个未定的整数.
---------------------------------------------------------------------------------
注意:不要让IANA官方页面上的"MIB/SNMP"语句把你迷惑了.通过上述表格获得的OID可以用于任何目的,当然也包括用来确定LDAP的方案元素(schema element).
---------------------------------------------------------------------------------
另外一种代替的办法是,从本国的权威机构(比如,ANSI,BSI)获取的OID名称空间也是有效的.

在试验阶段,你可以使用1.1作为你的顶层OID.因为OID 1.1 arc被认为是已死亡的名称空间.

8.2.2. (名称前缀)Name Prefix

除了给每个方案元素赋予一个唯一的对像标识符之外,你还应该给每个方案元素提供至少一个名称.名称应该具有描述性,同时还要保证不会和其它方案元素发生冲突.特别是,你选用的任何名称都不应该和现有的以及将来要使用到的Standard Track名称发生冲突.

为了减少(但无法消除)发生名称冲突的可能性,习惯的作法是给非Standard Track的名称加一些字母作为前缀,以使更改被局部化在你的组织内部.组织越小,前缀应该越长.

在下面的例子里面,我们选择了一个简短的前缀'my'(为了节省篇幅).像这样简短的前缀只适用于很大的全球化组织.通常情况下,我们推荐类似下面这样的 前缀,'deFirm'(德国的公司)或者'comExample'(和example.com相关联的组织的元素).

8.2.3. 本地方案文件(Local schema file)

objectclass和attributeTypes(译者注:原文如此,疑为笔误,似乎应该是attributetype)配置指令可以用来为目录中 的条目定义方案规则(schema rules).通常的惯例是创建一个文件包含你自己定制的方案项目.我们推荐你在/usr/local/etc/openldap/schema/下创建 一个local.schema文件并且将此文件包含到你的slapd.conf(5)文件中,注意应该把它放在其它include指令之后.

# include schema
        include /usr/local/etc/openldap/schema/core.schema
        include /usr/local/etc/openldap/schema/cosine.schema
        include /usr/local/etc/openldap/schema/inetorgperson.schema
        # include local schema
        include /usr/local/etc/openldap/schema/local.schema

8.2.4. 属性类型规范(Attribute Type Specification)

attributetype指令被用于定义新的属性类型.该指令使用同样的Attribute Type Description(按照RFC2252的定义),Attribute Type Description被子方案中子项目的attributeTypes属性使用,比如:

attributetype <RFC2252 Attribute Type Description>

这里的Attribute Type Description是通过下面的BNF定义的:

AttributeTypeDescription = "(" whsp
            numericoid whsp              ; AttributeType identifier
          [ "NAME" qdescrs ]             ; name used in AttributeType
          [ "DESC" qdstring ]            ; description
          [ "OBSOLETE" whsp ]
          [ "SUP" woid ]                 ; derived from this other
                                         ; AttributeType
          [ "EQUALITY" woid              ; Matching Rule name
          [ "ORDERING" woid              ; Matching Rule name
          [ "SUBSTR" woid ]              ; Matching Rule name
          [ "SYNTAX" whsp noidlen whsp ] ; Syntax OID
          [ "SINGLE-VALUE" whsp ]        ; default multi-valued
          [ "COLLECTIVE" whsp ]          ; default not collective
          [ "NO-USER-MODIFICATION" whsp ]; default user modifiable
          [ "USAGE" whsp AttributeUsage ]; default userApplications
          whsp ")"

AttributeUsage =
          "userApplications"     /
          "directoryOperation"   /
          "distributedOperation" / ; DSA-shared
          "dSAOperation"          ; DSA-specific, value depends on server

这里的whsp表示一个空格字符(' '),numericoid是一个全局唯一的点分十进制形式的OID(比如1.1.0),qdescrs是一个或多个名称,woid要么是一个名称,要么是一个OID,其后可选地跟着一个长度修饰符(比如{10}).

例如,core.schema中的name和cn属性类型是像这样定义的:

attributeType ( 2.5.4.41 NAME 'name'
                DESC 'name(s) associated with the object'
                EQUALITY caseIgnoreMatch
                SUBSTR caseIgnoreSubstringsMatch
                SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
        attributeType ( 2.5.4.3 NAME ( 'cn' 'commonName' )
                DESC 'common name(s) assciated with the object'
                SUP name )

注意到每一个属性类型都定义了属性的OID,一个简短的名称,和一个简要的描述.每个名称都是OID的一个别名.slapd(8)在返回结果的时候返回列出的第一个名称.

第一个属性,name,其syntax的值为directoryString(采用UTF-8格式编码的Unicode).这里的syntax的值是通过 OID指定的(1.3.6.1.4.1.1466.115.121.1.15等价于directoryString).同时指定了推荐的长度32768. 服务器应该支持这个长度的值,但是也可能会支持更长的值.如果没有在这个地方指定大小限制,服务器(像slapd)将会把这个地方忽略掉,也就是不会强制 加上大小限制.另外,equality和substring的匹配规则是忽略字母大小写.下表列出常用的syntax和匹配规则(OpenLDAP不仅支 持以下列出的所有syntax和匹配规则,它实际上提供的支持比这里列出的要多得多).

Table 8.3: Commonly Used Syntaxes
Name                 OID                 Description
boolean         1.3.6.1.4.1.1466.115.121.1.7     boolean value
directoryString     1.3.6.1.4.1.1466.115.121.1.15     Unicode (UTF-8) string
distinguishedName     1.3.6.1.4.1.1466.115.121.1.12     LDAP DN
integer         1.3.6.1.4.1.1466.115.121.1.27     integer
numericString     1.3.6.1.4.1.1466.115.121.1.36     numeric string
OID             1.3.6.1.4.1.1466.115.121.1.38     object identifier
octetString         1.3.6.1.4.1.1466.115.121.1.40     arbitary octets

Table 8.4: Commonly Used Matching Rules 
Name                 Type         Description
booleanMatch             equality     boolean
caseIgnoreMatch         equality     case insensitive, space insensitive
caseIgnoreOrderingMatch     ordering     case insensitive, space insensitive
caseIgnoreSubstringsMatch     substrings     case insensitive, space insensitive
caseExactMatch         equality     case sensitive, space insensitive
caseExactOrderingMatch     ordering     case sensitive, space insensitive
caseExactSubstringsMatch     substrings     case sensitive, space insensitive
distinguishedNameMatch     equality     distinguished name
integerMatch             equality     integer
integerOrderingMatch     ordering     integer
numericStringMatch         equality     numerical
numericStringOrderingMatch     ordering     numerical
numericStringSubstringsMatch     substrings     numerical
octetStringMatch             equality     octet string
octetStringOrderingStringMatch     ordering     octet string
octetStringSubstringsStringMatch     ordering     octet string
objectIdentiferMatch         equality     object identifier

第二个属性,cn,是name的一个子属性类型,因此它继承了name的syntax,匹配规则,和usage.而commonName则是cn的一个别名.

两个属性都没有对可拥有的值的个数进行限制.两者都可以由用户在具体应用中使用.两个属性都既没有定义obsolete也没有定义collective.

下面的子章节将给出几个范例.

8.2.4.1. myUniqueName

许多组织都为每一个用户提供了一个唯一的名称.尽管我们可以使用displayName(RFC2798),但是实际上这个属性是可以由用户更改的,而不 是由组织进行更改.我们可以简单的从inetorgperson.schema中拷贝displayName的定义,然后将OID,name,和 description替换成我们想要的,比如:

attributetype ( 1.1.2.1.1 NAME 'myUniqueName'
                DESC 'unique name with my organization'
                EQUALITY caseIgnoreMatch
                SUBSTR caseIgnoreSubstringsMatch
                SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
                SINGLE-VALUE )

然而,如果我们想把这个名称包含在name声明中的话[比如,(name=*Jane*)],那么取而代之的办法是将myUniqueName定义为name的子属性类型,比如,像这样定义:

attributetype ( 1.1.2.1.1 NAME 'myUniqueName'
                DESC 'unique name with my organization'
                SUP name )

8.2.4.2. myPhoto

许多组织都为每一位用户保存着一张照片.可以定义myPhoto属性来存放照片.当然,你也可以使用jpegPhoto (RFC2798)(或者它的子属性)来存放照片.但是,只有当你的照片是JPEG文件交换格式(File Interchange Format)的时候你才能这样做.另外一种办法是,定义一个syntax为Octet String的属性类型来存放照片,比如:

attributetype ( 1.1.2.1.2 NAME 'myPhoto'
                DESC 'a photo (application defined format)'
                SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
                SINGLE-VALUE )

在这种情况下,syntax没有指定照片的格式.它假设(注意这样的假设可能是不正确的)所有访问这个属性的应用程序都能够正确处理这个值.

如果你想支持多种照片格式,你可以给每种格式定义一个单独的属性类型,每一种格式的前面加上一些类型信息,或者采用ASN.1来描述这个值同时启用binary传输选项.

另一个替代的方法是定义一个属性来存放照片的URL位置,而非照片本身.你可以采用labeledURI (RFC2079)来建立这样一种模型,或者简单地创建该属性类型的子属性类型,比如,像这样进行定义:

attributetype ( 1.1.2.1.3 NAME 'myPhotoURI'
                DESC 'URI and optional label referring to a photo'
                SUP labeledURI )

8.2.5. 对象类规范(Object Class Specification)

objectclasses(译者注:原文如此,疑为笔误,似乎应该是objectclass)指令用来定义新的对像类型.该指令使用同样的Object Class Description (按照RFC2252中的定义),Object Class Description在子方案的子条目中被objectClasses属性使用,比如:

objectclass <RFC2252 Object Class Description>

这里的Object Class Description通过下面的BNF定义:

ObjectClassDescription = "(" whsp
                numericoid whsp      ; ObjectClass identifier
                [ "NAME" qdescrs ]
                [ "DESC" qdstring ]
                [ "OBSOLETE" whsp ]
                [ "SUP" oids ]       ; Superior ObjectClasses
                [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]
                        ; default structural
                [ "MUST" oids ]      ; AttributeTypes
                [ "MAY" oids ]       ; AttributeTypes
                whsp ")"

这里的whsp表示一个空格字符(' '),numericoid是一个全局唯一的点分十进制形式的OID(比如1.1.0),qdescrs是一个或多个名称,而oids则要么是一个或多个名称,要么是一个或多个OID.

8.2.5.1. myPhotoObjec

如果要定义一个辅助的对像类以允许我们在前面定义的myPhoto能够赋给任何已存在的条目.可以像下面这样:

objectclass ( 1.1.2.2.1 NAME 'myPhotoObject'
                DESC 'mixin myPhoto'
                AUXILIARY
                MAY myPhoto )

8.2.5.2. myPerson

如果你的组织想要用一个专有的结构化的对像类来实例化用户.你可以从一个已存在的person类继承,比如从inetOrgPerson (RFC2798)对像类继承,然后加上任何你需要的额外属性.

objectclass ( 1.1.2.2.2 NAME 'myPerson'
                DESC 'my person'
                SUP inetOrgPerson
                MUST ( myUniqueName $ givenName )
                MAY myPhoto )

这个对像类继承了inetOrgPerson中的所有必需/可选的属性类型,但同时还要求必需有myUniqueName和givenName属性,而myPhoto属性则是可选的.

8.2.6. OID宏定义(OID Macros)

为了使OID的管理和使用变得轻松,slapd(8)支持对像标识符宏定义(Object Identifier macros).指令objectIdentifier用来使一个宏等价于某一个对应的OID.而这个OID可以是从已有的OID宏定义中获得的. objectIdentifier在slapd.conf(5)的语法是这样的:

objectIdentifier <name> { <oid> | <name>[:<suffix>] }

下面描述了如何定义一组OID宏,同时涉及了如何在定义方案元素的时候使用OID宏定义:

objectIdentifier myOID  1.1
        objectIdentifier mySNMP myOID:1
        objectIdentifier myLDAP myOID:2
        objectIdentifier myAttributeType        myLDAP:1
        objectIdentifier myObjectClass  myLDAP:2
        attributetype ( myAttributeType:3 NAME 'myPhotoURI'
                DESC 'URI and optional label referring to a photo'
                SUP labeledURI )
        objectclass ( myObjectClass:1 NAME 'myPhotoObject'
                DESC 'mixin myPhoto'
                AUXILIARY
                MAY myPhoto )

本节将主要关注OpenLDAP的API,我在这里用的软件包是python-ldap,这是一个OpenLDAP的C语言接口的包装版本.鉴于python代码良好的可读性,我不会对代码进行详尽的说明.若有任何疑问请参考相关文档.

在前几节中,我们编写了一个ldif文件,depart.ldif,文件内容如下.然后我们通过ldapadd将depart.ldif文件中的条目添加到了目录树中.在本节中我们将要进行的所有操作都是在这个基础上进行的.

# depart.ldif
# THIS LDIF CAN ONLY BE CREATED AND MODIFIED BY ROCKINS
dn: o=dormforce.net,c=cn
o: dormforce.net
objectClass: organization

dn: ou=cert,o=dormforce.net,c=cn
ou: cert
objectClass: organizationalUnit

dn: ou=noc,o=dormforce.net,c=cn
ou: noc
objectClass: organizationalUnit

dn: ou=synx,o=dormforce.net,c=cn
ou: synx
objectClass: organizationalUnit

dn: ou=member,o=dormforce.net,c=cn
ou: member
objectClass: organizationalUnit

dn: cn=ChenYunChuan,ou=member,o=dormforce.net,c=cn
cn: ChenYunChuan
cn: Rockins
sn: 006
carLicense: A006
departmentNumber: 1
displayName: ChenYunChuan
employeeNumber: 006
employeeType: worker
givenName: Chen
homePhone: 028-82851445
mail: ybc2084@163.com
mobile: 123456789
roomNumber: RunXin 1#330
jpegPhoto:< file:///tmp/hero.jpeg
objectClass: organizationalPerson
objectClass: inetOrgPerson

1.绑定到LDAP服务器:

#!/usr/bin/env python
# file: bind.py

import ldap

ld = ldap.initialize("ldap://202.115.22.157:389")
ld.simple_bind_s("","")
print "bind to LDAP SERVER success"
ld.unbind_s()

我把这个脚本命名为bind.py,每一行的含义是:

#!/usr/bin/env python
表示调用python解释器来运行该文件
# file: bind.py
表明该文件名为bind.py
import ldap
导入ldap模块,和java中的import作用大致相同
ld = ldap.initialize("ldap://202.115.22.157:389")
初始化到LDAP服务器的连接,服务器地址是202.115.22.157(我的机器),端口号是389
ld.simple_bind_s("","")
匿名绑定到服务器上
print "bind to LDAP SERVER success"
在屏幕上打印一行以表明成功绑定到LDAP服务器
ld.unbind_s()
解除与LDAP服务器的绑定

2.搜索条目:

#!/usr/bin/env python
# file: search.py
import ldap

ld = ldap.initialize("ldap://202.115.22.157:389")
ld.simple_bind_s("","")
print "bind to LDAP SERVER success"
print ld.search_s("o=dormforce.net, c=cn", ldap.SCOPE_SUBTREE, "ou=synx")
ld.unbind_s()

这里只比上面多了一行:
print ld.search_s("o=dormforce.net, c=cn", ldap.SCOPE_SUBTREE, "ou=synx")
这一行的作用是在o=dormforce.net, c=cn的子树范围内搜索synx组织单元

此脚本的运行结果如下:
bind to LDAP SERVER success
[('ou=synx,o=dormforce.net,c=cn', {'objectClass': ['organizationalUnit'], 'ou': ['synx']})]
返回给我们的是ou=synx,o=dormforce.net,c=cn条目的各种属性,和我们在depart.ldif文件中所写的信息是一致的.

3.添加条目:

#!/usr/bin/env python
# file: add.py
import ldap
import ldap.modlist

ld = ldap.initialize("ldap://202.115.22.157:389")
ld.simple_bind_s("cn=root,o=dormforce.net,c=cn","secret")
print "root bind to LDAP SERVER success"
attrDict = {'objectClass': ['organizationalUnit'], 'ou': ['test']}
addList =  ldap.modlist.addModlist(attrDict)
ld.add_s("ou=test,o=dormforce.net, c=cn", addList)
ld.unbind_s()

这里,有几行需要做特别说明:

import ldap.modlist
导入ldap.modlist,用于根据条目的属性来构造属性列表
ld.simple_bind_s("cn=root,o=dormforce.net,c=cn","secret")
在目录中添加条目是一个特权操作,需要管理员权限,因此以cn=root绑定到服务器,口令是secret
attrDict = {'objectClass': ['organizationalUnit'], 'ou': ['test']}
这是条目所拥有的属性及其对应的属性值,表示为字典中嵌套列表的形式,'ou'是字典的键,而['test']是属性ou对应的值
addList =  ldap.modlist.addModlist(attrDict)
根据attrDict构造何时的列表addList
ld.add_s("ou=test,o=dormforce.net, c=cn", addList)
将条目ou=test,o=dormforce.net, c=cn添加到LDAP目录中,addList是条目的属性

运行此脚本后我们用ldapsearch进行验证:
cyc@cyc ~/py $ldapsearch "ou=test"
# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: ou=test
# requesting: ALL
#

# test, dormforce.net, cn
dn: ou=test,o=dormforce.net,c=cn
objectClass: organizationalUnit
ou: test

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

显然,该条目已经添加到目录中了.

4.修改条目:

#!/usr/bin/env python
# file: modify.py
import ldap
import ldap.modlist

ld = ldap.initialize("ldap://202.115.22.157:389")
ld.simple_bind_s("cn=root,o=dormforce.net,c=cn","secret")
print "root bind to LDAP SERVER success"
oldAttrDict = {'objectClass': ['organizationalUnit'], 'ou': ['test']}
newAttrDict = {'objectClass': ['organizationalUnit'],                 'ou':    ['test','experiment'],
        'description': ['this is just a experiment organizationUnit']}
modList =  ldap.modlist.modifyModlist(oldAttrDict,newAttrDict)
ld.modify_s("ou=test,o=dormforce.net, c=cn", modList)
ld.unbind_s()

这里和添加条目唯一的不同是需要把oldAttrDict和newAttrDict同时传给ldap.modlist.modifyModlist()以生成一个修改条目所需的列表,然后调用ld.modify_s()修改即可.

运行此脚本后我们通过ldapsearch验证:
cyc@cyc ~/py $ldapsearch 'ou=experiment'
# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: ou=experiment
# requesting: ALL
#

# test, dormforce.net, cn
dn: ou=test,o=dormforce.net,c=cn
objectClass: organizationalUnit
ou: test
ou: experiment
description: this is just a experiment organizationUnit

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

显然,修改操作是成功的.

5.删除条目:

#!/usr/bin/env python
# file: delete.py
import ldap
import ldap.modlist

ld = ldap.initialize("ldap://202.115.22.157:389")
ld.simple_bind_s("cn=root,o=dormforce.net,c=cn","secret")
print "root bind to LDAP SERVER success"
ld.delete_s("ou=test,o=dormforce.net, c=cn")
ld.unbind_s()

这里,唯一需要说明的是ld.delete_s("ou=test,o=dormforce.net, c=cn")将从目录中把ou=test,o=dormforce.net, c=cn删除.就是这么简单.

我们仍然用ldapsearch进行验证:

cyc@cyc ~/py $ldapsearch 'ou=test'
# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: ou=test
# requesting: ALL
#

# search result
search: 2
result: 0 Success

# numResponses: 1

显然,ou=test,o=dormforce.net, c=cn以及从目录中删除了.

参考链接:
[url]http://python-ldap.sourceforge.net/[/url]
[url]http://www.python.org[/url]
[url]http://www.openldap.org[/url]

因为本节讨论的内容不太适合放到前面的几篇里面,故而在这里单独列出,本节内容涉及的范围可能比较杂.同时我也不准备就这一节的内容进行深入讨论.如果感兴趣请参阅相关书籍或链接.

1.安全考虑: 为了保证服务器的安全性,一致性和完整性,OpenLDAP提供了许多额外的功能,如TLS,SSL,Kerberos,SASL,TCP wrapper,关于安全考虑请参见<>第九到十一章:[url]http://www.openldap.org/doc/admin22/security.html[/url]

2.备份和同步: 关于OpenLDAP服务器的备份和同步请参见<>第十三和十四章:[url]http://www.openldap.org/doc/admin22/replication.html[/url]

3.中文编码: 如果要在目录中加入UTF支持,在*nix平台下请试用iconv(1),关于iconv(1)命令请参见*nix手册;另外,在Linux Programmer's Manual中有关于iconv(3)函数的说明.

(全文完)

陈云川的OPENLDAP系列的更多相关文章

  1. openldap系列

    openldap系列 阅读视图 系列介绍 openldap系列目录 1. 系列介绍 本系列文档大部分来自于郭大勇老师的<OpenLDAP实战指南>,少部分来自于互联网.所有文档均已经过本人 ...

  2. Linux服务器部署系列之七—OpenLDAP篇

    LDAP(轻量级目录访问服务),通过配置这个服务,我们也可以在linux下面使用目录的形式管理用户,就像windows下面的AD一样,方便我们管理.下面我们就一起来配置openldap服务.本文运行环 ...

  3. 豪情-CSS解构系列之-新浪页面解构-01

    目录: 一. 新浪的布局特点 二. 内容细节的特点 三. 其中相关的一些基础技术点 1. 常见布局方法 2. 布局要点 3. Debugger误区 4.列表 5.字体颜色 6.CSS选择符 7.CSS ...

  4. 西川善司【神秘海域(Uncharted)】的图形分析

          本文是为传播0月8日发售的[神秘海域 合集]魅力而短篇连载的第2回,这次主要集中在神秘海域系列的图形的技术方面.原文链接在 http://weekly.ascii.jp/elem/000/ ...

  5. 【Fate/kaleid liner 魔法少女☆伊莉雅】系列中实践的、新世代的动画摄影工作流

          通常的日本动画的摄影中,是以追加Cell(celluloid 赛璐珞)与背景的合成滤镜处理为主,而在[Fate/kaleid liner 魔法少女☆伊莉雅]系列的,加入了自己使用3DCG软 ...

  6. OpenLDAP使用疑惑解答及使用Java完成LDAP身份认证

    导读 LDAP(轻量级目录访问协议,Lightweight Directory Access Protocol)是实现提供被称为目录服务的信息服务.目录服务是一种特殊的数据库系统,其专门针对读取,浏览 ...

  7. javacpp-opencv图像处理系列:国内车辆牌照检测识别系统(万份测试车牌识别准确率99.7%以上,单次平均耗时39ms)

    javaCV图像处理系列: 一.javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置.大小.粗度.翻转.平滑等操作 二.javaCV图像处理之2:实时视频添 ...

  8. Linux服务器部署系列之八—Sendmail篇

    Sendmail是目前Linux系统下面用得最广的邮件系统之一,虽然它存在一些不足,不过,目前还是有不少公司在使用它.对它的学习,也能让我们更深的了解邮件系统的运作.下面我们就来看看sendmail邮 ...

  9. LDAP落地实战(四):Jenkins集成OpenLDAP认证

    前几篇分文章分别介绍了OpenLDAP的部署管理和维护以及svn.git的接入,今天我们再下一城接入jenkins. 前情提要:LDAP系列文章 LDAP落地实战(一):OpenLDAP部署及管理维护 ...

随机推荐

  1. .Net Core微服务系列--服务发现

    什么是服务发现 首先我们先思考一个问题,当我们在浏览器中输入一个域名比如baidu.com,然后发生了什么才能让我们访问到百度的网页?简单来说,浏览器会首先从主机的hosts文件中查看是否有baidu ...

  2. nginx部署为HTTP代理支持CONNECT模式

    有个软件要走http代理,想着部署nginx起来用,结果发现用不了: 而用ccproxy的话,一切正常: 抓包分析了下,是CONNECT模式的请求 从nginx的官网http://nginx.org/ ...

  3. css3之字体@font-face

    @font-face能够加载服务器端的字体文件,让浏览器端可以显示用户电脑里没有安装的字体. 浏览器支持 表格中的数字表示支持该属性的第一个浏览器版本号. Firefox, Chrome, Safar ...

  4. 常用有三种json解析jackson、fastjson、gson。

    jackson依赖包 <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -- ...

  5. Odoo中Qweb使用入门

    参考 可参考官网例子https://doc.odoo.com/trunk/web/qweb/或 http://thierry-godin.developpez.com/openerp/tutorial ...

  6. Mysql8+mybatisGenerator (mysql 8的逆向工程)

    最近试了一下mysql8的逆向工程工具 1.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOC ...

  7. JavaSE_01_Exception类

    1.1 异常概念 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象.Java ...

  8. Java虚拟机原理图解-- 1.2、class文件中的常量池

    了解JVM虚拟机原理 是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述,很难给 ...

  9. Intelij Idea 2016破解

    在注册时选择License server,输入http://www.iteblog.com/idea/key.php,点击OK

  10. Django之数据库连接与建模

    Django数据库链接(这里以Mysql为例) 需要准备 Django1.10 pip install django==1.10 -i https://pypi.tuna.tsinghua.edu.c ...