Batch 3: SQL Databases LDAP YAML ← Full TOC

← LDAP Index

🏢 Active Directory — AD-specific attributes, queries, and authentication

Active Directory (AD) is Microsoft's LDAP implementation with proprietary extensions. It adds attributes like sAMAccountName, userPrincipalName, memberOf, and userAccountControl. Domain join, Group Policy, and Kerberos are AD-specific.

📋 Reference

ItemSyntax / ValueDescription
sAMAccountNamesAMAccountName: aliceWindows/AD login name (pre-Windows 2000)
userPrincipalNameuserPrincipalName: alice@example.comUPN — modern AD login: user@domain
distinguishedNamedistinguishedName: CN=Alice Smith,OU=Users,DC=example,DC=comAD-style DN uses CN= not uid=
CN=CN=Alice Smith,OU=Users,DC=example,DC=comAD uses CN for user RDN, not uid
OU=OU=Engineering,DC=example,DC=comOrganizational Unit in AD
DC=DC=example,DC=comDomain Component in AD
memberOfmemberOf: CN=Developers,OU=Groups,DC=example,DC=comGroups user belongs to (AD attribute)
userAccountControluserAccountControl: 512Bitmask: 512=enabled, 514=disabled, 66048=enabled+no_expire
accountExpiresaccountExpires: 0 / 132xxxAD account expiration timestamp
pwdLastSetpwdLastSet: 00=must change at next logon, -1=never expires
lastLogonlastLogon: 132xxxLast logon timestamp (not replicated between DCs)
lastLogonTimestamplastLogonTimestamp: 132xxxLast logon (replicated — use this for reports)
objectCategoryobjectCategory: CN=Person,CN=Schema,...AD object category (more specific than objectClass)
objectSIDobjectSID: S-1-5-21-xxxSecurity Identifier — unique across domain
primaryGroupIDprimaryGroupID: 513RID of primary group (Domain Users = 513)
AD bindDOMAIN\\username or username@domain.comAD bind formats
Global Catalogport 3268 (ldap) / 3269 (ldaps)AD Global Catalog — search across all domains
AD filter users(&(objectCategory=person)(objectClass=user))Find all user accounts in AD
AD enabled users(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))Active (non-disabled) users
AD group members(memberOf=CN=groupname,OU=Groups,DC=domain,DC=com)Direct group membership
AD nested groups(memberOf:1.2.840.113556.1.4.1941:=CN=group,...)Recursive/nested group membership
LDAP_MATCHING_RULE_IN_CHAIN1.2.840.113556.1.4.1941OID for recursive memberOf search
Kerberos bindldap3 with SASL GSSAPIUse Kerberos ticket for authentication

💡 Example

# Active Directory LDAP queries
# pip install ldap3

from ldap3 import Server, Connection, SUBTREE, ALL_ATTRIBUTES

# AD connection
AD_HOST   = "dc01.example.com"
AD_DOMAIN = "EXAMPLE"
AD_USER   = "binduser@example.com"    # or EXAMPLE\binduser
AD_PASS   = "password"
AD_BASE   = "DC=example,DC=com"

server = Server(AD_HOST, port=636, use_ssl=True, get_info="ALL")
conn   = Connection(server, user=AD_USER, password=AD_PASS, auto_bind=True)

# ── 1. Find all enabled users ─────────────────────────────────
conn.search(
    AD_BASE,
    # enabled users only (bit 2 of userAccountControl NOT set)
    "(&(objectCategory=person)(objectClass=user)"
    "(!(userAccountControl:1.2.840.113556.1.4.803:=2)))",
    search_scope = SUBTREE,
    attributes   = ["sAMAccountName","displayName","mail","department",
                    "title","lastLogonTimestamp","memberOf"]
)
print(f"Found {len(conn.entries)} enabled users")
for user in conn.entries[:5]:
    print(f"  {str(user.sAMAccountName):15} {str(user.displayName):25} {str(user.mail)}")

# ── 2. Find user by email ─────────────────────────────────────
conn.search(AD_BASE,
    f"(&(objectClass=user)(mail=alice@example.com))",
    attributes=["sAMAccountName","displayName","memberOf","department"])

if conn.entries:
    alice = conn.entries[0]
    print(f"\nUser: {alice.displayName.value}")
    print(f"Department: {alice.department.value}")
    print("Groups:")
    for group_dn in alice.memberOf.values:
        # Extract CN from group DN
        cn = group_dn.split(',')[0].replace('CN=','')
        print(f"  - {cn}")

# ── 3. Get all members of a group ────────────────────────────
group_dn = "CN=Developers,OU=Groups,DC=example,DC=com"
conn.search(AD_BASE,
    f"(&(objectClass=user)(memberOf={group_dn}))",
    attributes=["sAMAccountName","displayName","mail"])
print(f"\nDevelopers group members ({len(conn.entries)}):")
for m in conn.entries:
    print(f"  {str(m.sAMAccountName):15} {str(m.displayName)}")

# ── 4. Authenticate user (AD login) ───────────────────────────
def ad_authenticate(username, password, domain="example.com"):
    try:
        test_server = Server(AD_HOST, port=636, use_ssl=True)
        test_conn   = Connection(test_server,
            user=f"{username}@{domain}",
            password=password,
            auto_bind=True)
        test_conn.unbind()
        return True, "Authentication successful"
    except Exception as e:
        return False, str(e)

ok, msg = ad_authenticate("alice", "correct_password")
print(f"\nAuth result: {ok} — {msg}")

# ── 5. Get users who haven't logged in 90 days ───────────────
import datetime
cutoff = datetime.datetime.now() - datetime.timedelta(days=90)
# AD timestamp = 100-nanosecond intervals since Jan 1, 1601
ad_timestamp = int((cutoff - datetime.datetime(1601, 1, 1)).total_seconds() * 10**7)

conn.search(AD_BASE,
    f"(&(objectClass=user)(lastLogonTimestamp<={ad_timestamp}))",
    attributes=["sAMAccountName","lastLogonTimestamp"])
print(f"\nUsers not logged in 90+ days: {len(conn.entries)}")

conn.unbind()
# python3 ad_demo.py  (requires AD/LDAP server access)

← Python LDAP  |  🏠 Index  |  OpenLDAP Setup →