【LDAP環境構築:7】 Pythonでユーザー追加・検索・変更・削除まで

参考：http://www.grotan.com/ldap/python-ldap-samples.html

```py:ldap_client.py
#!/usr/bin/python
# -*- encoding=utf8 -*-

import ldap
from   ldap   import modlist

#----------------------------------------------------------
# LdapClient
#----------------------------------------------------------
class LdapClient(object):

    #----------------------------------------------------------
    # INIT
    #----------------------------------------------------------
    def __init__(self, ldap_host, ldap_base_dn, ldap_manager_password):

        self.ldap_base_dn       = ldap_base_dn
        self.ldap_manager_dn    = 'cn=Manager,%s' % ldap_base_dn
        self.ldap               = ldap.initialize(ldap_host)
        self.ldap.simple_bind_s(self.ldap_manager_dn, ldap_manager_password)

    #----------------------------------------------------------
    # SEARCH
    #----------------------------------------------------------
    def search_group(self, ldap_search_name):

        ldap_search_dn = 'ou=Group,%s'  % self.ldap_base_dn
        ldap_search_cn = 'cn=%s'        % ldap_search_name

        return self._search(ldap_search_dn, ldap_search_cn)

    def search_people(self, ldap_search_name):

        ldap_search_dn = 'ou=People,%s' % self.ldap_base_dn
        ldap_search_cn = 'uid=%s'       % ldap_search_name

        return self._search(ldap_search_dn, ldap_search_cn)

    def _search(self, ldap_search_dn, ldap_search_cn):

        row_ldif_list = self.ldap.search_s(ldap_search_dn,
                                           ldap.SCOPE_SUBTREE,
                                           ldap_search_cn,
                                           None)

        now_ldif_dict = {}

        for dn, entry in row_ldif_list:
            for _key, _value in entry.items():
                if _key != 'objectClass' :
                    now_ldif_dict[_key] =  _value[0]

            yield (dn, now_ldif_dict)

    #----------------------------------------------------------
    # ADD
    #----------------------------------------------------------
    def add_group(self, **keywords):

        keywords['objectclass'] = ['posixGroup', 'top']
        ldap_group_dn           = 'cn=%s,ou=Group,%s' % (keywords['cn'], self.ldap_base_dn)
        ldap_group_ldif         = modlist.addModlist(keywords)

        return self._add(ldap_group_dn, ldap_group_ldif)

    def add_people(self, **keywords):

        keywords['objectclass'] = ['account', 'posixAccount', 'top', 'shadowAccount']
        ldap_people_dn          = 'uid=%s,ou=People,%s' % (keywords['cn'], self.ldap_base_dn)
        ldap_people_ldif        = modlist.addModlist(keywords)

        return self._add(ldap_people_dn, ldap_people_ldif)

    def _add(self, dn, ldif):

        try :
            self.ldap.add_s(dn, ldif)
            return (0, dn)

        except ldap.ALREADY_EXISTS:
            return (1, 'already exists %s' % dn)

    #----------------------------------------------------------
    # DELETE
    #----------------------------------------------------------
    def delete_people(self, delete_name):

        ldap_delete_dn  = 'uid=%s,ou=People,%s' % (delete_name, self.ldap_base_dn)

        return self._delete(ldap_delete_dn)

    def delete_group(self, delete_name):

        ldap_delete_dn = 'cn=%s,ou=Group,%s'    % (delete_name, self.ldap_base_dn)

        return self._delete(ldap_delete_dn)

    def _delete(self, delete_dn):
        try :
            self.ldap.delete_s(delete_dn)
            return (0, delete_dn)

        except ldap.NO_SUCH_OBJECT, e:
            return (1, 'no such object %s' % delete_dn)

    #----------------------------------------------------------
    # MODIFY
    #----------------------------------------------------------
    def modify_people(self, name, change_ldif_dict):

        dn, now_ldif_dict = self.search_people(name).next()

        return self._modify(dn, now_ldif_dict, change_ldif_dict)


    def modify_group(self, name, change_ldif_dict):

        dn, now_ldif_dict = self.search_group(name).next()

        return self._modify(dn, now_ldif_dict, change_ldif_dict)


    def _modify(self, dn, now_ldif_dict, change_ldif_dict):

        new_ldif_dict = {}
        old_ldif_dict = {}

        for _key, _value in change_ldif_dict.items():
            if now_ldif_dict.has_key(_key):
                old_ldif_dict[_key] = now_ldif_dict[_key]
                new_ldif_dict[_key] = _value

        try :
            ldif = modlist.modifyModlist(old_ldif_dict, new_ldif_dict)
            self.ldap.modify_s(dn, ldif)
            return (0, dn, '%s > %s'  % (str(old_ldif_dict), str(new_ldif_dict)))

        except Exception, e:
            return (1, dn, e)

#----------------------------------------------------------
# SSHA python seeded salted sha password
# http://www.openldap.org/faq/data/cache/347.html
# > challenge_password = makeSecret('testing123')
# > checkPassword(challenge_password, 'testing123')
#----------------------------------------------------------
import os
import hashlib
from base64 import encodestring as encode
from base64 import decodestring as decode

def makeSecret(password):
    salt = os.urandom(4)
    h = hashlib.sha1(password)
    h.update(salt)
    return "{SSHA}" + encode(h.digest() + salt)

def checkPassword(challenge_password, password):
    challenge_bytes = decode(challenge_password[6:])
    digest = challenge_bytes[:20]
    salt = challenge_bytes[20:]
    hr = hashlib.sha1(password)
    hr.update(salt)
    return digest == hr.digest()

#----------------------------------------------------------
# TEST
#----------------------------------------------------------
def test():


    _LDAP_HOST      = 'ldap://192.168.1.11'
    _MANAGER_PASS   = 'xxxxxxxx'
    _BASE_DN        = 'dc=example,dc=com'

    a = LdapClient(_LDAP_HOST, _BASE_DN, _MANAGER_PASS)

    test_people(a)
    test_group(a)

def test_people(a):

    name      = 'user01'
    user_dict = { 'uid'           : name              ,
                  'cn'            : name              ,
                  'userPassword'  : makeSecret('pass'),
                  'uidNumber'     : '500'             ,
                  'gidNumber'     : '500'             ,
                  'homeDirectory' : '/home/hoge'      ,
                  'loginShell'    : '/bin/sh'         ,
                  'description'   : 'Description'     }

    print '\n#=== PEOPLE' + '=' * 80
    print '\n#------ SEARCH' + '-' * 80
    for i in a.search_people('*'):
        print '  %s' % i[0]

    print '\n#------ ADD' + '-' * 80
    print a.add_people(**user_dict)

    print '\n#------ MODIFY' + '-' * 80
    print a.modify_people(name, {'gidNumber':'1001'})

    print '\n#------ MODIFY' + '-' * 80
    print a.modify_people(name, {'userPassword': makeSecret('abc123')})

    print '\n#------ SEARCH' + '-' * 80
    for i in a.search_people('*'):
        print '  %s' % i[0]

    print '\n#------ DELETE' + '-' * 80
    print a.delete_people(name)

def test_group(a):

    name       = 'group01'
    group_dict = { 'cn'         : name   ,
                   'gidNumber'  : '9999' ,}

    print '\n#=== GROUP' + '=' * 80
    print '\n#------ SEARCH' + '-' * 80
    for i in a.search_group('*'):
        print '  %s' % i[0]

    print '\n#------ ADD' + '-' * 80
    print a.add_group(**group_dict)

    print '\n#------ MODIFY' + '-' * 80
    print a.modify_group(name, {'gidNumber':'1001'})

    print '\n#------ SEARCH' + '-' * 80
    for i in a.search_group('*'):
        print '  %s' % i[0]

    print '\n#------ DELETE' + '-' * 80
    print a.delete_group(name)

if __name__ == '__main__' : test()
```
>結果
>
```
#=== PEOPLE================================================================================
>
#------ SEARCH--------------------------------------------------------------------------------
  uid=testuser,ou=People,dc=example,dc=com
>
#------ ADD--------------------------------------------------------------------------------
(0, 'uid=user01,ou=People,dc=example,dc=com')
>
#------ MODIFY--------------------------------------------------------------------------------
(0, 'uid=user01,ou=People,dc=example,dc=com', "{'gidNumber': '500'} > {'gidNumber': '1001'}")
>
#------ MODIFY--------------------------------------------------------------------------------
(0, 'uid=user01,ou=People,dc=example,dc=com', "{'userPassword': '{SSHA}3kXMTM_X5Clv1KZIAQ9vcsUZ6mswW48Q'} > {'userPassword': '{SSHA}YEVxhhsIT3OedWqlrgjQXReAeznLSiHV'}")
>
#------ SEARCH--------------------------------------------------------------------------------
  uid=testuser,ou=People,dc=example,dc=com
  uid=user01,ou=People,dc=example,dc=com
>
#------ DELETE--------------------------------------------------------------------------------
(0, 'uid=user01,ou=People,dc=example,dc=com')
>
#=== GROUP================================================================================
>
#------ SEARCH--------------------------------------------------------------------------------
  cn=testuser,ou=Group,dc=example,dc=com
>
#------ ADD--------------------------------------------------------------------------------
(0, 'cn=group01,ou=Group,dc=example,dc=com')
>
#------ MODIFY--------------------------------------------------------------------------------
(0, 'cn=group01,ou=Group,dc=example,dc=com', "{'gidNumber': '9999'} > {'gidNumber': '1001'}")
>
#------ SEARCH--------------------------------------------------------------------------------
  cn=testuser,ou=Group,dc=example,dc=com
  cn=group01,ou=Group,dc=example,dc=com
>
#------ DELETE--------------------------------------------------------------------------------
(0, 'cn=group01,ou=Group,dc=example,dc=com')
```