def search(self, search_filter, attributes=None, search_scope=ldap3.SUBTREE, raise_exc=True): """ Search Active Directory using an LDAP filter. :param str search_filter: the LDAP search filter to use :kwarg list attributes: a list of LDAP attributes to search for :kwarg str search_scope: the LDAP search scope to use :kwarg bool raise_exc: raise an exception if the search yields no results :return: the ldap3 formatted response from Active Directory :rtype: list """ if not self.connection.bound: raise ADError('You must be logged into LDAP to search') msg = ( 'Searching Active Directory with "{0}" and the following attributes: {1}' .format(search_filter, ', '.join(attributes or []))) self.log('debug', msg) try: search_succeeded = self.connection.search( self.base_dn, search_filter, search_scope=search_scope, attributes=attributes) except ldap3.core.exceptions.LDAPAttributeError: msg = ( 'An invalid LDAP attribute was requested when searching for "{0}" with ' 'attributes: {1}').format(search_filter, ', '.join(attributes)) self.log('error', msg, exc_info=True) raise ADError(self.failed_search_error) if search_succeeded and self.connection.response: return self.connection.response self.log( 'error', 'The search for "{0}" did not yield any results'.format( search_filter)) if raise_exc: raise ADError(self.failed_search_error)
def get_sam_account_name(self, guid): """ Get a user's distinguished name from their GUID. :param str guid: the GUID of the user to search for :return: the user's sAMAccountNmae :rtype: str """ search_filter = '(&(objectClass=user)(objectGUID={0}))'.format(guid) results = self.search(search_filter, ['sAMAccountName']) if 'attributes' in results[0]: return results[0]['attributes']['sAMAccountName'] else: self.log( 'error', 'The user with the GUID {0} couldn\'t be found in Active Directory' .format(guid)) raise ADError('The user couldn\'t be found in Active Directory')
def login(self, username, password): """ Login to Active Directory. :param str username: the Active Directory username :param str password: the Active Directory password """ if self.connection.bound: msg = 'The login method was called but the connection is already bound. Will reconnect.' self.log('debug', msg) self.connection.unbind() self.connection.open() domain = self._get_config('AD_DOMAIN') if '@' in username or '\\' in username or 'CN=' in username: self.connection.user = username else: if self.connection.authentication == ldap3.NTLM: self.connection.user = '******'.format(domain, username) else: self.connection.user = '******'.format(username, domain) self.connection.password = password svc_account = self._get_config('AD_SERVICE_USERNAME') == username if not self.connection.bind(): if svc_account: self.log('error', 'The service account failed to login') raise ADError(self.unknown_error_msg) else: self.log( 'info', 'The user "{0}" failed to login'.format( self.connection.user)) raise Unauthorized( 'The username or password is incorrect. Please try again.') else: if svc_account: self.log('info', 'The service account logged in successfully') else: self.log('info', 'The user logged in successfully')
def connection(self): """ Return an unauthenticated LDAP connection to Active Directory. :return: an LDAP connection to Active Directory :rtype: ldap3.Connection """ if self._connection: return self._connection ldap_url = self._get_config('AD_LDAP_URI') server = ldap3.Server(ldap_url, allowed_referral_hosts=[('*', False)], connect_timeout=3) self._connection = ldap3.Connection(server) if self._get_config('AD_USE_NTLM', raise_exc=False): msg = 'Configuring the Active Directory connection to use NTLM authentication' self.log('debug', msg) self._connection.authentication = ldap3.NTLM else: msg = 'Configuring the Active Directory connection to use SIMPLE authentication' self.log('debug', msg) self._connection.authentication = ldap3.SIMPLE try: msg = 'Connecting to Active Directory with the URL "{0}"'.format( ldap_url) self.log('debug', msg) self._connection.open() except LDAPSocketOpenError: msg = 'The connection to Active Directory with the URL "{0}" failed'.format( ldap_url) self.log('error', msg, exc_info=True) raise ADError( 'The connection to Active Directory failed. Please try again.') return self._connection
def get_loggedin_user(self, raise_exc=True): """ Get the logged in user's username. :kwarg bool raise_exc: raise an exception if the connection isn't bound :return: a the logged in user's user name :rtype: str """ if self.connection.bound: user = self.connection.extend.standard.who_am_i() if not self._get_config('TESTING', raise_exc=False): # AD returns the username as DOMAIN\username, so this gets the sAMAccountName return user.split('\\')[-1] else: # We are using ldap3's mocking, which returns the distinguished name, so derive the # sAMAccountName from that return user.split('CN=')[-1].split(',')[0] elif raise_exc: self.log( 'error', 'You must be logged in to get the logged in user\'s username') raise ADError(self.unknown_error_msg)