Используйте скрипт Python для управления удаленным сервером LDAP

Справочная информация: я работаю над API для централизации создания и управления пользователями для нескольких ресурсов (например, Google Apps, Dropbox и т. Д.). На виртуальной машине Linux я разработал API и веб-интерфейс, который позволяет мне (и моим коллегам) проверять подлинность учетных записей пользователей этих служб и управлять ими. Следующее, что мне нужно интегрировать, - это наш Active Directory, который размещен на удаленном Windows Server 2008.

Я пытался использовать python-ldap для подключения и извлечения / изменения информации, но у меня были проблемы с ошибками операций DIR_ERROR (при попытке запроса пользователей) и ошибками NAMING_VIOLATION (при попытке добавления пользователей).

* Код, основанный на http://www.grotan.com/ldap/python-ldap-samples.html, вопросах stackru и документации по python-ldap. Связывающий код, который, как я считаю, работает:

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, [])

Скрипт запроса для пользователей: (пробовал без привязки, как предложено в статье, но получил "Для выполнения этой операции необходимо выполнить успешную привязку при подключении".)

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':'Ошибка операций }

Добавить скрипт пользователя: (используя 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"}

* Это заставило меня думать, что проблема может быть в порте, поэтому я изменил строку инициализации на l = ldap.initialize("ldap://serverIp:389/") похож на два других сценария.

Теперь я получаю: ldap.NAMING_VIOLATION: {'info': "00002099: NameErr: DSID-0305109C, проблема 2005 (NAMING_VIOLATION), данные 0, лучшее соответствие:\n\t'dc=adtest, dc=local'\n", 'desc': 'Нарушение имен'}

Кроме того, я возился с добавлением ou и uid к attrs, но без изменений по ошибке.

Что я делаю не так или что я могу попытаться сделать по-другому? Спасибо за любую помощь / предложения!

редактирование: я проверил свой сервер, и порт 636 правильно настроен для разрешения трафика Secure LDAP, поэтому я не знаю, почему это дало мне ошибки, отличные от нормального LDAP. edit2: я попытался изменить следующую строку в моем сценарии добавления dn="cn=test,dc=adtest.local"

и новый вывод (трассировка стека), который у меня есть (я добавил оператор печати, чтобы показать, что связывание действительно происходит сейчас до ошибки):
(97, [], 1, [])
Traceback (последний вызов был последним):
Файл "test2.py", строка 21, в <module>
l.add_s(дп, 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, все, время ожидания)
Файл "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", строка 469, в результате 2
resp_type, resp_data, resp_msgid, resp_ctrls = self.result3 (msgid, все, время ожидания)
Файл "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", строка 476, в результате 3
resp_ctrl_classes = resp_ctrl_classes
Файл "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", строка 483, в результате 4
ldap_result = self._ldap_call (self._l.result4, msgid, все, время ожидания,add_ctrls,add_intermediates,add_extop)
Файл "/usr/local/lib/python2.7/dist-packages/ldap/ldapobject.py", строка 106, в _ldap_call
результат = func (* args, ** kwargs)
ldap.REFERRAL: {'info': 'Referral: \ nldap: //adtest.local/cn=test,dc=adtest.local', 'desc': 'Referral'}

1 ответ

Решение

Рабочий запрос поиска!
Кредит для:
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 (чтобы включить пользователя), я переключился на LDAPS, используя порт 636, который я включил на сервере, добавив службы сертификации Active Directory (* требуется перезапустить сервер).
Кроме того, вам нужно включить строку 'ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,0)' перед инициализацией.

Рабочая Добавить пользователя
Кредит для:
как установить lockoutTime и пароль пользователя 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()
Другие вопросы по тегам