views:

252

answers:

3

Hi,

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 bothersome as rest of the code is already in 3.0 format.

+1  A: 

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

ChristopheD
I know that there is no python-ldap module for python 3 but I secretly hoped that there would be some other module that supported LDAP protocol. Well no win here yet.
Calmar
Hmm, I don't see any reason to switch to python 3 other than 3 > 2.6
GregS
this post is about 9 months old. any update on the answer?
iamrohitbanga
+1  A: 

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.

Todd
Could you please post the code if you have already wrote it.
Calmar
A: 

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.

Noufal Ibrahim