Phase 3 PR 31 — Live-LDAP integration test + Active Directory compatibility #30
Reference in New Issue
Block a user
Delete Branch "phase-3-pr31-live-ldap-ad-compat"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes LMX follow-up #4 with 6 live-bind tests against the dev GLAuth instance (skipped cleanly when unreachable) and makes the authenticator configurable for Active Directory along the way — normal deployment target for this project.
Live-bind coverage (
LdapUserAuthenticatorLiveTests)writeopuser'smemberOfmaps throughGroupToRoletoWriteOperate— the exact stringWriteAuthzPolicy.IsAllowedexpects.adminuser surfaces all four mapped roles (WriteOperate + WriteTune + WriteConfigure + AlarmAck), provingmemberOfparsing doesn't stop after the first match.All 6 pass against a running GLAuth at
localhost:3893; skip cleanly with an operator-facing message when the port is unreachable.Active Directory compatibility
Wiring the live-smoke surfaced a real gap: the authenticator's hard-coded
(uid=<name>)filter didn't match GLAuth (keys users bycn), and wouldn't match AD either (keys users bysAMAccountName). Fix: newLdapOptions.UserNameAttribute(defaultuidfor RFC 2307 backcompat) drives the filter. Regression guard inLdapUserAuthenticatorAdCompatTestspins the default so a future 'helpful' rename can't silently break anyone.Also:
LdapOptionsxml-doc expanded with an AD cheat-sheet covering DC FQDN, LDAPS port 636 under LDAP-signing enforcement, dedicated read-only service account,sAMAccountNamevsuserPrincipalNamevscntrade-offs, ADmemberOfDN shape (CN=Group,OU=...,DC=...with theCN=RDN stripped forGroupToRolelookup), and an explicit 'nested groups NOT expanded' call-out (LDAP_MATCHING_RULE_IN_CHAIN/tokenGroupsis a future enhancement, not a config change).docs/security.md§"Active Directory configuration" adds a complete ADappsettings.jsonsnippet with realistic group names (OPCUA-Operators→WriteOperate, etc.), LDAPS on,AllowInsecureLdap: false, and operator-facing notes on each field.LdapUserAuthenticatorAdCompatTests(5 deterministic unit guards):ExtractFirstRdnValueparses AD-styleCN=OPCUA-Operators,OU=...,DC=...DNs correctly (case-preserving — operators'GroupToRolekeys stay readable).Domain Users).ou=<group>,ou=groups,...shape (GLAuth) so one extractor tolerates both memberOf formats common in the field.EscapeLdapFilterescapes the RFC 4515 injection set (\,*,(,),\0) — verifies a login likeadmin)(cn=*can't break out of the filter.UserNameAttributeregression guard.Test posture
Category=LiveLdap: 6 pass / 0 fail against running GLAuth (skips cleanly without).Deferred
Session-identity end-to-end check (drive a full OPC UA UserName session, then read a 'whoami' node to verify the role landed on
RoleBasedIdentity). That needs a test-only address-space node and is scoped for a separate PR.Active Directory compatibility. LdapOptions xml-doc expanded with a cheat-sheet covering Server (DC FQDN), Port 389 vs 636, UseTls=true under AD LDAP-signing enforcement, dedicated read-only service account DN, sAMAccountName vs userPrincipalName vs cn trade-offs, memberOf DN shape (CN=Group,OU=...,DC=... with the CN= RDN stripped to become the GroupToRole key), and the explicit 'nested groups NOT expanded' call-out (LDAP_MATCHING_RULE_IN_CHAIN / tokenGroups is a future authenticator enhancement, not a config change). docs/security.md §'Active Directory configuration' adds a complete appsettings.json snippet with realistic AD group names (OPCUA-Operators → WriteOperate, OPCUA-Engineers → WriteConfigure, OPCUA-AlarmAck → AlarmAck, OPCUA-Tuners → WriteTune), LDAPS port 636, TLS on, insecure-LDAP off, and operator-facing notes on each field. LdapUserAuthenticatorAdCompatTests (5 unit guards): ExtractFirstRdnValue parses AD-style 'CN=OPCUA-Operators,OU=...,DC=...' DNs correctly (case-preserving — operators' GroupToRole keys stay readable); also handles mixed case and spaces in group names ('Domain Users'); also works against the OpenLDAP ou=<group>,ou=groups shape (GLAuth) so one extractor tolerates both memberOf formats common in the field; EscapeLdapFilter escapes the RFC 4515 injection set (\, *, (, ), \0) so a malicious login like 'admin)(cn=*' can't break out of the filter; default UserNameAttribute regression guard. Test posture — Server.Tests Unit: 43 pass / 0 fail (38 prior + 5 new AD-compat guards). Server.Tests LiveLdap category: 6 pass / 0 fail against running GLAuth (would skip cleanly without). Server build clean, 0 errors, 0 warnings. Deferred: the session-identity end-to-end check (drive a full OPC UA UserName session, then read a 'whoami' node to verify the role landed on RoleBasedIdentity). That needs a test-only address-space node and is scoped for a separate PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>