Skip to content Skip to sidebar Skip to footer

Does Python 3 Have LDAP Module?

I am porting some Java code to Python and we would like to use Python 3 but I can't find LDAP module for Python 3 in Windows. This is forcing us to use 2.6 version and it is bother

Solution 1:

You may use ldap3 module (formerly known as python3-ldap), it runs on python3 really well and requires no external C dependances. Also it can correctly handle both unicode and byte data in ldap records (in early versions there was a trouble with jpegPhoto field, now everything is fine)


Solution 2:

If you're running this on Windows, you can get LDAP to work in Python 3.1 by using the ADO access method via Mark Hammond's PyWin32.

To test this, I installed ActiveState Python 3.1, then installed PyWin32 for Python 3.1

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

I was then able to run LDAP queries using a module I wrote that is based on this LDAP code from the ActiveState Python Cookbook:

Recipe 511451: Dump all Active Directory Information using LDAP scripting by Manuel Garcia

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

although now that I look at it I realize I completely rewrote my module just using his code as an example.


Update

Here is my LDAPList module and another support module to convert user access bit codes into something a tiny bit more english-like:

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)))

###

Both modules have some usage examples and should be fairly easy to figure out, but let me know if you have any questions or comments.


Solution 3:

There is a Pure Python implementation of an LDAP client called Ldaptor. I don't think it's maintained though. If you really need it, you might be able to run 2to3 on this and port it.


Solution 4:

This answer is no longer accurate; see below for other answers.

Sorry to break this on you, but I don't think there is a python-ldap for Python 3 (yet)...

That's the reason why we should keep active development at Python 2.6 for now (as long as most crucial dependencies (libs) are not ported to 3.0).


Post a Comment for "Does Python 3 Have LDAP Module?"