从零开始devops-python ldap

from ldap3 import Server, Connection
from passlib.hash import ldap_salted_sha1 as ssha
from ldap3 import MODIFY_REPLACE


def pass_encrypt(passwd):
    return ssha.encrypt(passwd, salt_size=16)


class LDAPUtil:

    def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
                 password=None, use_ssl=None, search_ougroup=None,
                 search_filter=None, attr_map=None, auth_ldap=None):

        # config
        if use_settings_config:
            self._load_config_from_settings()
        else:
            self.server_uri = server_uri
            self.bind_dn = bind_dn
            self.password = password
            self.use_ssl = use_ssl
            self.search_ougroup = search_ougroup
            self.search_filter = search_filter
            self.attr_map = attr_map
            self.auth_ldap = auth_ldap
        self.conn = self.get_connection()

    def _load_config_from_settings(self):
        self.server_uri = AUTH_LDAP_SERVER_URI
        self.bind_dn = AUTH_LDAP_BIND_DN
        self.password = AUTH_LDAP_BIND_PASSWORD
        self.use_ssl = AUTH_LDAP_START_TLS
        self.search_ougroup = AUTH_LDAP_SEARCH_OU
        self.search_filter = AUTH_LDAP_SEARCH_FILTER
        self.attr_map = AUTH_LDAP_USER_ATTR_MAP
        self.auth_ldap = AUTH_LDAP

    def get_user_by_username(self, username):
        """
        通过用户名获取ldap用户,
        :param username: 用户名
        :return:  dict {}
        """
        conn = self.conn
        search_ougroup = str(self.search_ougroup).split("|")
        for search_ou in search_ougroup:
            ok = conn.search(
                search_ou, self.search_filter % ({"user": username}),
                attributes=list(self.attr_map.values())
            )
            if not ok:
                error = "Search no entry matched in ou {}".format(search_ou)
                return False, error
            return True, self.entry_to_dict(conn.entries[0])

    def entry_to_dict(self, entry):
        """
        entry 转 dict对象
        :param entry: ldap entry对象
        :return:
        """
        user_item = {}
        for attr, mapping in self.attr_map.items():
            user_item[mapping] = getattr(entry, mapping).value or ''
        return user_item

    def user_dict_to_entry_dict(self, user_dict):
        """
        用户对象dict转成entry对象dict
        :param user_dict: 用户对象
        :return:
        """
        user_item = {}
        for attr, mapping in self.attr_map.items():
            value = user_dict.get(attr, '')
            if value == True or value == False or value == 'true' or value == 'false':
                value = str(value).capitalize()
            user_item[mapping] = value
        if not user_item.get('uid'):
            user_item['uid'] = user_item['cn']
            user_item['sn'] = user_item['cn']
        if not user_item.get('cn'):
            user_item['cn'] = user_item['uid']
            user_item['sn'] = user_item['uid']
        if user_item.get('userPassword'):
            user_item['userPassword'] = pass_encrypt(user_item['userPassword'])
        return user_item

    def update_user(self, username, attr):
        """
        更新dn的entry属性
        :param username: ldap username
        :param attr: ldap 用户 dict
        :return:
        """
        changes_dic = {}
        dn = "{},{}".format(str(self.search_filter % ({"user": username})).strip('()'), self.search_ougroup)
        for k, v in attr.items():
            if not self.conn.compare(dn=dn, attribute=k, value=v):
                # password hash will be changed all time
                if k == 'userPassword':
                    continue
                changes_dic.update({k: [(MODIFY_REPLACE, [v])]})
        if changes_dic:
            self.conn.modify(dn=dn, changes=changes_dic)
            return True, self.conn.result['description']
        else:
            return False, "no changed"

    def update_user_password(self, username, passwd):
        """
        更新用户密码
        :param dn: ldap username
        :param passwd: 密码
        :return: bool, msg
        """
        dn = "{},{}".format(str(self.search_filter % ({"user": username})).strip('()'), self.search_ougroup)
        changes_dic = {'userPassword': [(MODIFY_REPLACE, [pass_encrypt(passwd)])]}
        self.conn.modify(dn=dn, changes=changes_dic)
        return self.conn.result

    def compare_attr(self, dn, attr, value):
        """
        比较员工指定的某个属性
        :param dn:
        :param attr:
        :param value:
        :return:
        """
        res = self.conn.compare(dn=dn, attribute=attr, value=value)
        return res

    def create_user(self, user_item):
    """
    创建用户
    :param user_item: 用户字典
    """
        if user_item.get('uid') or user_item.get('cn'):
            cn_user = "uid={},{}".format(user_item.get('uid'), self.search_ougroup)
            ok = self.conn.add(
                cn_user, ['inetOrgPerson', 'top', 'extensibleObject'], user_item
            )
            if not ok:
                error = "Add entry {} error: {}".format(cn_user, self.conn.last_error)
                return False, error
            return True, ""
        error = "Add entry {} error: User entry must have uid/cn.".format(user_item)
        return False, error

    @staticmethod
    def get_or_construct_email(user_item):
        if not user_item.get('email', None):
            if '@' in user_item['username']:
                email = user_item['username']
            else:
                email = '{}@{}'.format(
                    user_item['username'], EMAIL_SUFFIX)
        else:
            email = user_item['email']
        return email

    def create_or_update_users(self, user_items):
        """
        创建或更新用户list
        :param user_items: 用户字典
        :return:
        """
        succeed = failed = 0
        failed_list = []
        succeed_list = []
        for user_item in user_items:
            username = user_item.get('uid') or user_item.get('cn')
            if username:
                exist, data = self.get_user_by_username(username)
                if exist:
                    ok, error = self.update_user(username, user_item)
                else:
                    ok, error = self.create_user(user_item)
                if not ok:
                    failed += 1
                    failed_list.append(username)
                else:
                    succeed += 1
                    succeed_list.append(username)
            else:
                pass
        result = {'total': len(user_items), 'succeed': succeed, 'failed': failed, 'failed_list': failed_list, 'succeed_list': succeed_list}
        return result

    def _ldap_entry_to_user_item(self, entry):
        user_item = {}
        for attr, mapping in self.attr_map.items():
            if not hasattr(entry, mapping):
                continue
            user_item[attr] = getattr(entry, mapping).value or ''
        return user_item

    def get_connection(self):
        server = Server(self.server_uri, use_ssl=self.use_ssl)
        conn = Connection(server, self.bind_dn, self.password, read_only=False)
        conn.bind()
        return conn

    def get_search_user_items(self):
        conn = self.get_connection()
        user_items = []
        search_ougroup = str(self.search_ougroup).split("|")
        for search_ou in search_ougroup:
            ok = conn.search(
                search_ou, self.search_filter % ({"user": "*"}),
                attributes=list(self.attr_map.values())
            )
            if not ok:
                error = "Search no entry matched in ou {}".format(search_ou)
                raise Exception(error)

            for entry in conn.entries:
                user_item = self._ldap_entry_to_user_item(entry)
                user = self.get_user_by_username(user_item['username'])
                user_item['existing'] = bool(user)
                user_items.append(user_item)

        return user_items

    def del_user(self, username):
        dn = "uid={},{}".format(username, self.search_ougroup)
        ok = self.conn.delete(dn)
        if ok:
            return True, ''
        return False, self.conn.last_error


if __name__ == '__main__':
    AUTH_LDAP = False
    AUTH_LDAP_SERVER_URI = 'ldap://oneops.top:389'
    AUTH_LDAP_BIND_DN = 'cn=Manager,dc=oneops,dc=com'
    AUTH_LDAP_BIND_PASSWORD = 'xxxx'
    AUTH_LDAP_SEARCH_OU = 'ou=People,dc=oneops,dc=com'
    AUTH_LDAP_SEARCH_FILTER = '(uid=%(user)s)'
    AUTH_LDAP_START_TLS = False
    AUTH_LDAP_USER_ATTR_MAP = {"username": "uid", "name": "displayName",
                               "email": "mail", "phone": "mobile",
                               "is_active": "olcAllows", "password": "userPassword"}
    EMAIL_SUFFIX = "oneops.top"
    ldap_tool = LDAPUtil()
    # print(ldap_tool.get_user_by_username(username='xx'))
    print(ldap_tool.get_search_user_items())
    entry_dict = ldap_tool.user_dict_to_entry_dict({'username': 'xiaomao', 'name': '薛', 'email': '[email protected]', 'phone': '136xx', 'is_active': 'true'})
    # print(entry_dict)
    # print(ldap_tool.create_user(entry_dict))

    # dn = "uid=test,ou=People,dc=kingxunlian,dc=com"
    # print(ldap_tool.update_user('xxx', entry_dict))
    # print(ldap_tool.del_user('test'))

猜你喜欢

转载自www.cnblogs.com/franzlistan/p/12933622.html