Skip to content

Instantly share code, notes, and snippets.

@othmanalikhan
Last active March 10, 2021 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save othmanalikhan/5b3cf976f33975d9777fc3c259eee068 to your computer and use it in GitHub Desktop.
Save othmanalikhan/5b3cf976f33975d9777fc3c259eee068 to your computer and use it in GitHub Desktop.
Quick Start: Using Python LDAP3 library with Microsoft AD. Includes reading/writing attributes, and AD password resetting, (LAST UPDATED: 2021-03)
"""
IMPROVING DEVELOPMENT WORKFLOW
------------------------------
Problem: LDAP error messages from MS AD are vague and sometimes even misleading.
Solution:
A. Replicate the operations you would like to code manually using `LDAP Admin` and `Wireshark` and study.
1.1. Using `LDAP Admin`, ensure you connect to the LDAP server without using TLS.
1.2. Run Wireshark in the background and apply a `ldap` filter.
1.3. Perform the needed LDAP operations in `LDAP Admin` and study corresponding packets in `Wireshark`.
1.4. Now develop/modify code until the code LDAP packets match the 'correct' packets from `LDAP Admin`.
B. Enable verbose logging in ldap3 which dumps the LDAP packets even before sending to OS kernel.
2.1. See the code below for details.
"""
import logging
import ldap3
from ldap3 import Server, Connection, ALL, NTLM
# Dumps LDAP messages and other goodies. Change "NETWORK" to "EXTENDED" for even more spam.
from ldap3.utils.log import set_library_log_detail_level, BASIC, NETWORK, EXTENDED
logging.basicConfig(filename='client_application.log', level=logging.DEBUG)
set_library_log_detail_level(NETWORK)
AD_SERVER = "domaincontroller.foods.com"
SEARCH_BASE = "OU=Users,OU=cakes,DC=foods,DC=com"
USER_MOCK = "CN=burger,DC=foods,DC=com"
PASS_MOCK = "WhereMyBurgerChez?"
def loginUsingNTLM():
server = Server(AD_SERVER, get_info=ALL)
conn = Connection(server, user="foods\\burger", password=PASS_MOCK, authentication=NTLM, auto_bind=True)
# conn.start_tls()
print(conn)
def loginAsAnonymousUser():
server = Server(AD_SERVER, get_info=ALL)
conn = Connection(server, auto_bind=True)
# conn.start_tls()
print(conn)
def searchLDAP():
server = Server(AD_SERVER, get_info=ALL)
conn = Connection(server, user=USER_MOCK, password=PASS_MOCK, auto_bind=True)
# Method A
# conn.search(SEARCH_BASE, '(objectclass=person)')
# print(conn.entries)
# Method B (More Pythonic)
searchFilter = ldap3.ObjectDef("user", conn)
r = ldap3.Reader(conn, searchFilter, SEARCH_BASE)
r.search()
print(r)
def writeData():
server = Server(AD_SERVER, get_info=ALL)
connection = Connection(server, user=USER_MOCK, password=PASS_MOCK, auto_bind=True)
connection.start_tls()
searchFilter = ldap3.ObjectDef("user", connection)
reader = ldap3.Reader(connection, searchFilter, SEARCH_BASE)
reader.search()
writer = ldap3.Writer.from_cursor(reader)
user = None
for entry in writer:
print(entry)
if "automation" in entry.distinguishedName.value.lower():
user = entry
# Beware: Using += operators fails by AD if the attribute exists already!
user.sn = "cheese" # user.sn.changes[0] = ("MODIFY_REPLACE", ["cheese"])
# user.sn += "cheese" # user.sn.changes[0] = ("MODIFY_ADD", ["cheese"])
# user.sn -= "cheese" # user.sn.changes[0] = ("MODIFY_REMOVE", ["cheese"])
print(user.entry_changes)
writer.commit()
print(connection.result)
def resetPassword(newPassword):
server = Server(AD_SERVER, get_info=ALL)
connection = Connection(server, user=USER_MOCK, password=PASS_MOCK, auto_bind=True)
connection.start_tls()
searchFilter = ldap3.ObjectDef("user", connection)
reader = ldap3.Reader(connection, searchFilter, SEARCH_BASE)
reader.search()
dn = None
for entry in reader.search():
if "automation" in entry.distinguishedName.value.lower():
dn = entry.distinguishedName.value
if dn:
connection.extend.microsoft.modify_password(dn, newPassword)
print(connection.result)
else:
print("ERROR: Could not find the user starting with 'Automation'!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment