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': 'xiaomao@oneops.top', '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'))