Background: I am working on an API to centralize user creation and management for multiple resources(e.g. Google Apps, Dropbox, etc...). On a Linux VM, I developed an API and web interface that allows me(and my co-admins) to authenticate and manage user accounts for these services. The next thing I need to integrate is our Active Directory which is hosted on a remote Windows Server 2008.
I have been trying to use python-ldap to connect to and retrieve/modify information but have had issues with DIR_ERROR operations errors(when trying to query for users) and NAMING_VIOLATION errors(when trying to add users).
*Code based on http://www.grotan.com/ldap/python-ldap-samples.html, stackoverflow questions, and python-ldap documentation Binding code that I believe works:
import ldap
try:
l = ldap.open("serverip")
l.protocol_version = ldap.VERSION3
username = "myUserName@adtest.local"
password = "secret"
result = l.simple_bind(username, password)
print result
except ldap.LDAPError, e:
print e
which prints: (97, [], 1, [])
Query for users script: (tried without bind as suggested by article, but received "In order to perform this operation a successful bind must be completed on the connection.")
import ldap
try:
l = ldap.open("serverIp", port=389)
l.protocol_version = ldap.VERSION3
username = "myUserName@adtest.local"
password = "secret"
result = l.simple_bind(username, password)
print result
except ldap.LDAPError, e:
print e
# handle error however you like
baseDN = "ou=Users, o=adtest.local"
searchScope = ldap.SCOPE_SUBTREE
retrieveAttributes = None
searchFilter = "cn=*myUserName*"
try:
ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
result_set = []
while 1:
result_type, result_data = l.result(ldap_result_id, 0)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
print result_set
except ldap.LDAPError, e:
print e
which results in the following: (97, [], 1, []) {'info': '000020D6: SvcErr: DSID-031007DB, problem 5012 (DIR_ERROR), data 0\n','desc':'Operations error'}
Add user script:(using ldaps)
import ldap
import ldap.modlist as modlist
# Open a connection
l = ldap.initialize("ldaps://serverIp:636/")
# Bind/authenticate with a user with apropriate rights to add objects
l.simple_bind_s("myUserName@adtest.local","secret")
# The dn of our new entry/object
dn="cn=test,dc=adtest,dc=local"
# A dict to help build the "body" of the object
attrs = {}
attrs['objectclass'] = ['top','organizationalRole','simpleSecurityObject']
attrs['cn'] = 'test'
attrs['userPassword'] = 'aDifferentSecret'
attrs['description'] = 'test user'
# Convert our dict to nice syntax for the add-function using modlist-module
ldif = modlist.addModlist(attrs)
# Do the add-operation to the ldapserver
l.add_s(dn,ldif)
# Disconnect and free resources when done
l.unbind_s()
Which results in: ldap.SERVER_DOWN: {'info': 'A TLS packet with unexpected length was received.','desc': "Can't contact LDAP server"}
*This made me think that the port might be the problem, so I changed the initialize line to l = ldap.initialize("ldap://serverIp:389/")
similar to the other two scripts.
Now I get: ldap.NAMING_VIOLATION: {'info': "00002099: NameErr: DSID-0305109C, problem 2005 (NAMING_VIOLATION), data 0, best match of:\n\t'dc=adtest, dc=local'\n", 'desc': 'Naming violation'}
Additionally, I have messed around with adding the ou and uid to the attrs but no change in error.
What am I doing wrong or what could I try to do differently? Thank you for any help/suggestions!
edit: I checked my server, and port 636 is correctly set to allow Secure LDAP traffic, so I don't know why that was giving me different errors than the normal LDAP. edit2: I tried changing the following line in my add script dn="cn=test,dc=adtest.local"
and the new output(stack trace) I have is(I added the print statement in to show that the bind is actually taking place now before the error):
(97, [], 1, [])
Traceback (most recent call last):
File "test2.py", line 21, in <module>
l.add_s(dn,ldif)
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 202, in add_s
return self.result(msgid,all=1,timeout=self.timeout)
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 465, in result
resp_type, resp_data, resp_msgid = self.result2(msgid,all,timeout)
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 469, in result2
resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout)
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 476, in result3
resp_ctrl_classes=resp_ctrl_classes
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 483, in result4
ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)
File "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", line 106, in _ldap_call
result = func(*args,**kwargs)
ldap.REFERRAL: {'info': 'Referral:\nldap://adtest.local/cn=test,dc=adtest.local', 'desc': 'Referral'}
Working Query search!
Credit to:
http://www.linuxjournal.com/article/6988?page=0,0
I fixed the one mistake in my code, but still couldn't set certain properties because LDAP uses plain-text and does not allow private info to be sent without a secure connection. In order to add/modify user password info and userAccountControl flags(to enable a user), I switched to LDAPS using port 636, which I enabled on the server by adding Active Directory Certificate Services(*requires you to restart the server).
Additionally you need to include the 'ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,0)' line before you initialize.
Working Add User
Credit to:
how to set lockoutTime and password of a user of Active Directory