v1.12.0
This release closes the two most-requested gaps in the API: you can finally disable and re-enable user and computer accounts through a first-class method (instead of hand-rolling the userAccountControl bit flip yourself), and the privileged-account marker AD stamps on every member of a protected group is now surfaced as a plain field on the User struct.
What's new
Four new methods on *LDAP — DisableUser, EnableUser, DisableComputer, EnableComputer (each with a *Context variant). All four flip the ACCOUNTDISABLE bit (0x2) on the target entry's userAccountControl and leave every other UAC flag untouched. Implementation is a single read-modify-write: search the current value, OR or AND-NOT the bit, issue a Modify Replace. WORKSTATION_TRUST_ACCOUNT, NoPasswordExpiration, SmartCardRequired, everything else the entry was carrying — preserved.
All four are idempotent: a second disable on an already-disabled account is a no-op (target == current), not an error, and not a wasted round-trip. Callers can poll or retry without worrying about accidental toggling.
These are Active Directory only. OpenLDAP inetOrgPerson has no portable disable attribute, and the methods refuse to silently no-op — calls against a non-AD entry return a clear error: "userAccountControl attribute missing on … (not an Active Directory entry?)". Callers driving mixed directories should gate on Config.IsActiveDirectory.
One note on concurrency: the read-modify-write window is visible to parallel UAC writers on the same DN. For the ACCOUNTDISABLE bit in isolation the outcome converges to whichever write commits last — the same race any non-compare-and-swap LDAP modification has. Admin tools that need strict ordering should serialise their calls. Documented on the helper and in the CHANGELOG.
User.AdminCount bool, mapped from AD's adminCount attribute. AD sets this to 1 via adminSDHolder on every member of a protected group (Domain Admins, Enterprise Admins, Administrators, Account Operators, Backup Operators, Server Operators, Print Operators, Replicator, Schema Admins, Key Admins, Enterprise Key Admins, Read-Only Domain Controllers, Domain Controllers). Having it on the struct means clients can flag privileged accounts without walking nested group membership, without hard-coding English CN lists that break on localised directories (no more missing Domänen-Admins), and without a second search.
adminCount is added to the internal userFields attribute list, so every existing user search picks it up automatically — no caller-side changes required.
One caveat worth knowing: the attribute is sticky. AD does not clear adminCount when a user is removed from a protected group, so AdminCount == true means "is or was privileged at some point", which is still a strong signal for UI badging but not a perfect real-time membership check. Documented inline on the struct field.
ACCOUNTDISABLE exported constant (uint32 = 0x2) for anyone composing their own UAC writes. Lifting the magic number out of the docs and into the API means no more // 0x0002 = ADS_UF_ACCOUNTDISABLE comments in caller code.
Compatibility
Pure additive change. The new methods live alongside the existing API; the new struct field defaults to false when the directory doesn't return the attribute (which is always the case on OpenLDAP). Existing code continues to compile and behave identically.
Quality
Two new test files — disable_test.go and the adminCount cases added to users_mock_test.go — cover the value surface:
- The exported
ACCOUNTDISABLEconstant is pinned at0x2so a future refactor can't silently move it. - All four
*Contextmethods surface connection errors without panicking when the client has no live backend. - The read-modify-write bit arithmetic is extracted as a pure-function unit test matrix: set-disable, clear-disable, idempotent set, idempotent clear, and bit-preservation across unrelated flags — catches off-by-one bit errors that would otherwise only show up against a real AD.
adminCount = 1maps toAdminCount == true;0, missing, and the unexpected value2all map tofalse(strict-match semantics guard against garbled data).
Full unit suite green, golangci-lint run reports 0 issues.
See CHANGELOG.md for the same content in Keep-a-Changelog sections.