Python 3有LDAP模块吗?

时间:2009-12-30 20:56:48

标签: python ldap python-3.x

我正在将一些Java代码移植到Python中,我们希望使用Python 3,但是在Windows中找不到适用于Python 3的LDAP模块。

这迫使我们使用2.6版本,这很麻烦,因为其余代码已经是3.0格式。

4 个答案:

答案 0 :(得分:36)

您可以使用 ldap3 模块(以前称为 python3-ldap ),它在python3上运行得非常好,并且不需要外部C依赖。它还能正确处理ldap记录中的unicode和字节数据(在早期版本中,jpegPhoto字段存在问题,现在一切都很好)

答案 1 :(得分:2)

如果你在Windows上运行它,你可以通过Mark Hammond的PyWin32使用ADO访问方法让LDAP在Python 3.1中工作。

为了测试这个,我安装了ActiveState Python 3.1,然后为Python 3.1安装了PyWin32

http://sourceforge.net/projects/pywin32/files/

然后我能够使用我编写的基于ActiveState Python Cookbook中的LDAP代码的模块运行LDAP查询:

配方511451:使用Manuel Garcia的LDAP脚本转储所有Active Directory信息

http://code.activestate.com/recipes/511451/

虽然现在我看着它,但我意识到我只是以他的代码为例完全改写了我的模块。


<强>更新

这是我的LDAPList模块和另一个支持模块,用于将用户访问位代码转换为更像英语的东西:

LDAPList.py

# LDAPList.py
# Todd Fiske
# class to encapsulate accessing LDAP information

# 2009-03-18  first version
# 2010-01-04  updated for Python 3 (print functions, <> to !=)

import win32com.client
import UACCodes

ADS_SCOPE_SUBTREE = 2

class LDAPList():

  def __init__(self, sContext):
    self.Context = sContext # naming context, "DC=xyz,DC=org"

    self.objectCategory = ""
    self.objectClass = ""
    self.FilterClause = ""
    self.query = ""

    self.cn = None
    self.cm = None
    self.rs = None

  def SetCategory(self, sCategory):
    self.objectCategory = sCategory
    self.FilterClause = "where objectCategory = '%s'" % self.objectCategory

  def SetClass(self, sClass):
    self.objectClass = sClass
    self.FilterClause = "where objectClass = '%s'" % self.objectClass

  def open(self):
    self.query = "select * from 'LDAP://%s' %s order by displayName" % (self.Context, self.FilterClause)
    self.cn = win32com.client.Dispatch("ADODB.Connection")
    self.cm = win32com.client.Dispatch("ADODB.Command")

    self.cn.Open("Provider=ADsDSOObject")
    self.cm.ActiveConnection = self.cn
    self.cm.Properties["Page Size"] = 1000
    self.cm.Properties["Searchscope"] = ADS_SCOPE_SUBTREE

    self.cm.CommandText = self.query
    self.rs = self.cm.Execute()[0]

  def close(self):
    if self.rs is not None:
      self.rs.Close()
      self.rs = None

    if self.cm is not None:
      self.cm = None

    if self.cn is not None:
      self.cn.Close()
      self.cn = None

  def count(self):
    if self.rs is None:
      return -2
    return self.rs.RecordCount

  def more(self):
    if self.rs is None:
      return False
    return not self.rs.EOF

  def GetObject(self):
    if self.rs is None:
      return None
    return win32com.client.GetObject(self.rs.Fields["ADsPath"].Value)

  def next(self):
    if self.rs is None:
      return
    self.rs.MoveNext()

#----------

# helper functions

def NamingContext():
  # return default naming context
  root = win32com.client.GetObject("LDAP://RootDse")
  return root.get("DefaultNamingContext")

def AccountControl(obj):
  if obj.userAccountControl is not None:
    return obj.userAccountControl
  else:
    return 0

def ConvertUAC(nUAC):
  return UACCodes.ConvertUAC(nUAC)

def AccountActive(n):
  return (n & UACCodes.ADS_UF_ACCOUNTDISABLE) != UACCodes.ADS_UF_ACCOUNTDISABLE

def GetCategory(obj):
  # CN=Group,CN=Schema,CN=Configuration,DC=xyz,DC=org
  s = obj.objectCategory
  s = s.split(",")[0][3:]
  return s
  # s = "Group"

def GetGroups(obj):
  """
  ('CN=XYZ Staff Rockville,OU=Distribution Groups,DC=xyz,DC=org', 
  'CN=XYZ Staff,OU=Distribution Groups,DC=xyz,DC=org')
  """

  if obj.memberOf is None:
    return ""

  if type(obj.memberOf)==type(()):
    tGroups = obj.memberOf
  else:
    tGroups = (obj.memberOf,)
  return tGroups

def GetNameParts(obj):
  if obj.givenName is None:
    sFirst = ""
  else:
    sFirst = obj.givenName

  if obj.middleName is None:
    sMiddle = ""
  else:
    sMiddle = obj.middleName

  if obj.sn is None:
    sLast = ""
  else:
    sLast = obj.sn

  if sLast == "" and sFirst == "":
    if obj.name is not None:
      sName = obj.name
      sName = sName[3:]
      lParts = sName.split(" ")
      if len(lParts) == 1:
        "todo: split on embedded capital letter"
        print("single-part name: %s" % sName)
        sFirst = sName
      else:
        sLast = lParts[-1]
        sFirst = " ".join(lParts[:-1])

  return (sFirst, sMiddle, sLast)

def GetManager(obj):
  if obj.manager is None:
    return ""
  else:
    return obj.manager  

#----------

# test

if __name__ == "__main__":

  print
  print("testing LDAPList class")

  nc = NamingContext()
  print("context =", nc)

  ll = LDAPList(nc)
  ll.SetCategory('user')

  ll.open() # generates recordset
  print("query = %s" % ll.query)
  print("%d items" % ll.count())

  n = 0
  while (n < 10) and (ll.more()):
    o = ll.GetObject() # return 
    nUAC = AccountControl(o)
    print("%-30s  %-30s  %-30s  %-40s  %s" % (
      o.displayName, 
      o.name, 
      o.sAMAccountName, 
      UACCodes.ConvertUAC(nUAC), 
      GetManager(o)
    ))
    n += 1
    ll.next()

  ll.close()

###

UACCodes.py

# UACCodes.py
# Todd Fiske
# generated 2009-09-23 16:36:56 by BuildUACCodes.py
# updated 2010-01-04 for Python 3 (print functions)
# provide UAC constants, lookup list, and conversion function

import sys

# UAC Constants
ADS_UF_SCRIPT                                 = 0x00000001
ADS_UF_ACCOUNTDISABLE                         = 0x00000002
ADS_UF_HOMEDIR_REQUIRED                       = 0x00000008
ADS_UF_LOCKOUT                                = 0x00000010
ADS_UF_PASSWD_NOTREQD                         = 0x00000020
ADS_UF_PASSWD_CANT_CHANGE                     = 0x00000040
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED        = 0x00000080
ADS_UF_TEMP_DUPLICATE_ACCOUNT                 = 0x00000100
ADS_UF_NORMAL_ACCOUNT                         = 0x00000200
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT              = 0x00000800
ADS_UF_WORKSTATION_TRUST_ACCOUNT              = 0x00001000
ADS_UF_SERVER_TRUST_ACCOUNT                   = 0x00002000
ADS_UF_DONT_EXPIRE_PASSWD                     = 0x00010000
ADS_UF_MNS_LOGON_ACCOUNT                      = 0x00020000
ADS_UF_SMARTCARD_REQUIRED                     = 0x00040000
ADS_UF_TRUSTED_FOR_DELEGATION                 = 0x00080000
ADS_UF_NOT_DELEGATED                          = 0x00100000
ADS_UF_USE_DES_KEY_ONLY                       = 0x00200000
ADS_UF_DONT_REQUIRE_PREAUTH                   = 0x00400000
ADS_UF_PASSWORD_EXPIRED                       = 0x00800000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x01000000


# UAC short name lookup list
lUACCodes = [
  ("ADS_UF_SCRIPT"                                , 0x00000001, "script"),
  ("ADS_UF_ACCOUNTDISABLE"                        , 0x00000002, "disabled"),
  ("ADS_UF_HOMEDIR_REQUIRED"                      , 0x00000008, "homedir"),
  ("ADS_UF_LOCKOUT"                               , 0x00000010, "lockout"),
  ("ADS_UF_PASSWD_NOTREQD"                        , 0x00000020, "pwnotreqd"),
  ("ADS_UF_PASSWD_CANT_CHANGE"                    , 0x00000040, "pwcantchange"),
  ("ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED"       , 0x00000080, "encryptedpw"),
  ("ADS_UF_TEMP_DUPLICATE_ACCOUNT"                , 0x00000100, "dupaccount"),
  ("ADS_UF_NORMAL_ACCOUNT"                        , 0x00000200, "useracct"),
  ("ADS_UF_INTERDOMAIN_TRUST_ACCOUNT"             , 0x00000800, "interdomain"),
  ("ADS_UF_WORKSTATION_TRUST_ACCOUNT"             , 0x00001000, "workstation"),
  ("ADS_UF_SERVER_TRUST_ACCOUNT"                  , 0x00002000, "server"),
  ("ADS_UF_DONT_EXPIRE_PASSWD"                    , 0x00010000, "pwnoexpire"),
  ("ADS_UF_MNS_LOGON_ACCOUNT"                     , 0x00020000, "mnslogon"),
  ("ADS_UF_SMARTCARD_REQUIRED"                    , 0x00040000, "smartcard"),
  ("ADS_UF_TRUSTED_FOR_DELEGATION"                , 0x00080000, "trustdeleg"),
  ("ADS_UF_NOT_DELEGATED"                         , 0x00100000, "notdeleg"),
  ("ADS_UF_USE_DES_KEY_ONLY"                      , 0x00200000, "deskey"),
  ("ADS_UF_DONT_REQUIRE_PREAUTH"                  , 0x00400000, "nopreauth"),
  ("ADS_UF_PASSWORD_EXPIRED"                      , 0x00800000, "pwexpired"),
  ("ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION", 0x01000000, "trustauth"),
]


# UAC conversion function
def ConvertUAC(nUAC):
  s = ""
  for c in lUACCodes:
    if ((nUAC & c[1]) == c[1]):
      s = s + c[2] + " "
  return s

# test routine
if __name__ == "__main__":
  print("UACCodes Test")
  print("-------------")

  for n in [0, 512, 514, 65535]:
    print("%d = %s" % (n, ConvertUAC(n)))

  print
  for s in sys.argv[1:]:
    n = int(s)
    print("%d = %s" % (n, ConvertUAC(n)))

###

这两个模块都有一些使用示例,应该很容易理解,但如果您有任何问题或意见,请告诉我。

答案 2 :(得分:0)

有一个名为Ldaptor的LDAP客户端的纯Python实现。我不认为它保持不变。如果你真的需要它,你可以在此上运行2to3并移植它。

答案 3 :(得分:-1)

很抱歉打破这个问题,但我认为Python 3还没有python-ldap ...

这就是我们现在应该在Python 2.6上保持活跃开发的原因(只要大多数关键依赖项(libs)没有移植到3.0)。