Last active
March 10, 2021 17:55
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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