使用Python脚本管理远程LDAP服务器 [英] Use Python script to manage remote LDAP server

查看:155
本文介绍了使用Python脚本管理远程LDAP服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:
我正在开发一个API,以集中用户创建和管理多种资源(例如Google Apps,Dropbox等)。
在Linux VM上,我开发了API和Web界面,使我(和我的共同管理员)可以验证和管理这些服务的用户帐户。
接下来需要集成的Active Directory托管在远程Windows Server 2008上。

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.

我一直在尝试使用python-ldap来连接并检索/修改信息,但遇到DIR_ERROR操作错误(尝试查询用户)和NAMING_VIOLATION错误(尝试添加用户)时出现问题。

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

*基于的代码http://www.grotan.com/ldap/python-ldap-samples.html ,stackoverflow问题和python-ldap文档
我认为有效的绑定代码:

*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

打印:
(97,[],1,[])

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

其结果如下:
(97,[] ,1,[])
{'info ':'000020D6:SvcErr:DSID-031007DB,问题5012(DIR_ERROR),数据0\n','desc':'操作错误'}

which results in the following: (97, [], 1, []) {'info': '000020D6: SvcErr: DSID-031007DB, problem 5012 (DIR_ERROR), data 0\n','desc':'Operations error'}

添加用户脚本:(使用ldaps)

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

哪个结果在:
ldap.SERVER_DOWN:{'info':'接收到具有意外长度的TLS数据包。','desc':无法联系LDAP服务器}

Which results in: ldap.SERVER_DOWN: {'info': 'A TLS packet with unexpected length was received.','desc': "Can't contact LDAP server"}

*这使我认为端口可能是问题,因此我将初始化行更改为 l = ldap.initialize( ldap:// serverIp:389 /)类似于其他两个脚本。

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

现在我得到:
ldap.NAMING_VIOLATION:{'info': 00002099:NameErr:DSID -0305109C,问题2005(NAMING_VIOLATION),数据0,最佳匹配:\n\t'dc = adtest,dc = local'\n,'desc':'命名冲突'}

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'}

此外,我在outr和uid添加到attrs时一团糟,但没有错误更改。

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!

编辑:我检查了服务器,端口636正确设置为允许安全LDAP通信,所以我不这样做。不知道为什么这给了我与普通LDAP不同的错误。
edit2:我尝试在添加脚本
dn = cn = test,dc = adtest.local中更改以下行

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"

和我有新的输出(堆栈跟踪)(我添加了print语句,以表明绑定实际上是在错误发生之前现在进行的):

(97,[],1,[])< br>
追溯(最近一次通话最后一次):

文件 test2.py,第21行,位于< module> < br>
l.add_s(dn,ldif)

文件 /usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py,第202行,在add_s中

返回self.result(msgid,all = 1,timeout = self.timeout)

文件 /usr/local/lib/python2.7/dist-packages/ldap/ ldapobject.py,第465行,结果为

resp_type,resp_data,resp_msgid = self.result2(msgid,all,timeout)

文件 / usr / local / lib / python2 .7 / dist-packages / ldap / ldapobject.py,第469行,位于result2

resp_type,resp_data,resp_msgid,resp_ctrls = self.result3(msgid,all,timeout)

文件 / usr /local/lib/python2.7/dist-packages/ldap/ldapobject.py,第476行,在result3中

resp_ctrl_classes = resp_ctrl_classes

文件 / usr / local / lib /python2.7/dist-packages/ldap/ldapobject.py,第483行,在result4

ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates, add_extop)

文件 /usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py,第106行,在_ldap_call中

result = func(* args,** kwargs)

ldap.REFERRAL:{'info':'Referral:\nldap://adtest.local/cn=test,dc=adtest.local','desc': 'Referral'}

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'}

推荐答案

有效的查询搜索!

归功于:

http://www.linuxjournal.com/article / 6988?page = 0,0

import ldap

def main():

    keyword = "user_query"

    try:
        l = ldap.open(serverIp)
        l.simple_bind_s("myUserName@adtest.local", "password")
        print "successfully bound to server.\n"

        print "Searching..\n"
        my_search(l,keyword)
    except ldap.LDAPError, e:
        print "Couldn't connect. %s " % e

def my_search(l, keyword):
    #Base is for the DN(Distinguised Name) of the entry where the search should start
    base = "cn=Users,dc=adtest,dc=local"
    #Scope has three options, SUBTREE searches all sub-folder/directories
    scope = ldap.SCOPE_SUBTREE
    #filter consists of a cn(common name) and keyword.
    #putting asterisks around our keyword will match anything containing the string
    f = "cn=" + "*" + keyword + "*"
    #determines which attributes to return. Returns all if set to "None"
    retrieve_attributes = None

    count = 0
    result_set = []
    timeout = 0
    result = l.search_s(base, scope, f, retrieve_attributes)
    print result[0][1].keys()
    try:
        result_id = l.search(base, scope, f, retrieve_attributes)
        while 1:
            result_type, result_data = l.result(result_id, timeout)
            if(result_data == []):
                break
            else:
                if result_type == ldap.RES_SEARCH_ENTRY:
                    result_set.append(result_data)
        if len(result_set) == 0:
            print "No Results"
            return
        for i in range(len(result_set)):
            for entry in result_set[i]:
                try:
                    name = entry[1]['cn'][0]
                    count += 1
                    print str(count)+" "+name
                except:
                    pass
    except ldap.LDAPError, e:
        print e

if __name__=='__main__':
    main()

我修复了代码中的一个错误,但是仍然无法设置某些属性,因为LDAP使用纯文本,并且不允许在没有安全连接的情况下发送私人信息。
为了添加/修改用户密码信息和userAccountControl标志(以启用用户),我使用端口636切换到LDAPS,该端口通过添加Active Directory证书服务在服务器上启用(*需要您重新启动服务器。

另外,您需要在初始化之前添加'ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,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.

正在添加用户

功劳:

如何设置Active Directory用户的锁定时间和密码

import ldap
import ldap.modlist as modlist

ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,0)
l = ldap.initialize("ldaps://10.99.0.214:636")
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
l.set_option(ldap.OPT_NETWORK_TIMEOUT, 10.0)
t = l.simple_bind_s("myUserName@adtest.local","password")

dn="cn=TestUser,cn=Users,dc=adtest,dc=local"

#make a unicode password to set for user
unicode_pass = unicode('\"'+"userPwd"+'\"', 'iso-8859-1')
password_value = unicode_pass.encode('utf-16-le')

#What I set for my users, you can find more by looking through a user's properties on your DC.
attrs = {}
attrs['cn'] = 'TestUser'
attrs['displayName'] = 'TestUser'
attrs['givenName'] = 'Test'
attrs['mail'] = 'testuser@company.com'
attrs['name'] = 'Test User'
attrs['objectclass'] = ['top','person','organizationalPerson','user']
attrs['sAMAccountName'] = 'testuser'
attrs['sn'] = 'User'
attrs['unicodepwd'] = password_value
attrs['userPrincipalName'] = 'testuser@adtest.local'

ldif = modlist.addModlist(attrs)

l.add_s(dn,ldif)

#Now that the user is created and has a password(needs to meet AD requirements), they can be enabled 

#For full userAccountControl flag list:
#http://support.microsoft.com/en-us/kb/305144
mod_acct = [(ldap.MOD_REPLACE, 'userAccountControl', '66048')]
try:
    l.modify_s(dn, mod_acct)
except ldap.LDAPError, e:
    print e

l.unbind_s()

这篇关于使用Python脚本管理远程LDAP服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆