def getServer(): if not Server: return None if not SERVER or not SERVER[0]: return None for s in SERVER: server = Server(s, connect_timeout=1, use_ssl=True) if server.check_availability(): try: with Connection(server): return server except: continue for s in SERVER: server = Server(s, connect_timeout=1) if server.check_availability(): return server return None
def authenticate(self, userPrincipalName, password): """ [概要] 認証 [引数] userPrincipalName: ユーザ名 password: パスワード read_timeout: 取得タイムアウト値(秒) [戻り値] AD_AUTH結果コード(define.AD_AUTH.RESULT_*) """ # TODO:server_poolのほうが使える? server = None serverList = [] for host in self._hosts: server = Server(host, port=self._port, use_ssl=self._use_ssl, get_info='ALL', connect_timeout=self._connect_timeout) # 対象hostの応答がなければ次のhostへ if not server.check_availability() : continue # 応答があればそれを使用する logger.logic_log('LOSI11003', host) serverList.append(server) if len(serverList) == 0: # すべてのhostがNGの場合はエラー logger.logic_log('LOSM11001', ', '.join(self._hosts)) return AD_AUTH.RESULT_OTHER_ERROR try: # step1. domainに対する認証 conn = Connection( serverList, auto_bind=AUTO_BIND_NONE, client_strategy=SYNC, user=userPrincipalName, password=password, authentication=SIMPLE, check_names=True, receive_timeout=self._read_timeout ) if not conn.bind(): logger.logic_log('LOSM11002', conn.last_error, conn.result['message']) result_message = conn.result['message'] error_code = AD_AUTH.RESULT_OTHER_ERROR if AD_AUTH.REGEXP_INVALID_CREDENCIALS_MESSAGE.match(result_message): error_code = AD_AUTH.RESULT_INVALID_CREDENCIALS error_message = 'Authentication information error' if AD_AUTH.REGEXP_ACCOUNT_LOCKED_MESSAGE.match(result_message): error_code = AD_AUTH.RESULT_ACCOUNT_LOCKED error_message = 'Account lock status' logger.logic_log('LOSM11003', error_code, error_message) return error_code # step2. 検索文字列で指定される組織内に存在するか # TODO 管理者の認証のときは除外する? success = conn.search(search_base=self._search_path, search_filter='(userPrincipalName=%s)' % userPrincipalName, search_scope=SUBTREE) if not success or len(conn.entries) != 1: logger.logic_log('LOSM11004', self._search_path) return AD_AUTH.RESULT_INVALID_CREDENCIALS except Exception as e: logger.system_log('LOSM11005', server.host, userPrincipalName) return AD_AUTH.RESULT_OTHER_ERROR logger.system_log('LOSI11004', conn) # 成功時 self._conn = conn return AD_AUTH.RESULT_SUCCESS
def server_connect(self): server = Server(self.host, port=self.port, get_info=ALL) if not server.check_availability(): raise Exception('LDAP Server is not reachable') return server
class LdapOperations(object): #"single star" syntax ignores unspecified positional args # and provides keyword-only arguments, see PEP-3102 def __init__(self, type, *, server=None, domain=None, user=None, ntlm_hash=None, plaintext_pw=None, use_ssl=True, windows_user_format=False): if not server and user and domain: raise ML_Error( 'Server, user, and domain are required for LDAP Operations') self.user = user self.domain = domain if type == LdapServerType.Windows_AD: if ntlm_hash: self.ntlm_hash = ntlm_hash elif plaintext_pw: self.hash = self.ntlm_hash(plaintext_pw) else: raise ValueError( 'Windows AD requires ntlm_hash or plaintext_pw keyword.') self.server = Server(server, get_info=ALL) self.conn = Connection(self.server, user=self.domain + '\\' + self.user, password=self.hash, authentication=NTLM) elif type == LdapServerType.FreeIPA: # TODO: something that will actually work here. tls = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) self.server = Server(server, get_info=ALL, tls=tls, use_ssl=use_ssl) self.conn = Connection(self.server, authentication=SASL, sasl_mechanism='EXTERNAL') elif type == LdapServerType.Generic_LDAP: self.server = Server( server, get_info=ALL, ) self.conn = Connection(self.server, user=user, password=plaintext_pw) else: raise LdapServerTypeError(type) ''' Ldap3 doesn't raise exceptions on connection errors, so we make sure everything worked below ''' if self.server.check_availability(): pass else: raise LdapConnectError("Failed to connect to server") if self.conn.bind(): pass else: raise LdapBindError("Incorrect Login information") def serverInfo(self): return self.server.info def ntlm_hash(self, pw): #correct function for ntlm hash h = hashlib.new('md4', pw.encode('utf-16le')).hexdigest() ## For some reason, this is the desired format (LM:NTLM): return '00000000000000000000000000000000:' + h def users_in_group(self, baseDN, groupDN, attributes=['cn']): self.conn.search(search_base=baseDN, search_filter='(memberOf=' + groupDN + ')', search_scope=SUBTREE, attributes=attributes) return self.conn.entries def find_dn_from_username(self, basedn, username, attributes=['userPrincipalName', 'user']): search_string = '(|' for a in attributes: search_string = search_string + '({}={})'.format(a, username) search_string = search_string + ')' self.conn.search(basedn, search_string, attributes='distinguishedName') try: return self.conn.entries[0].distinguishedName.value except IndexError: return None