chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the Rider Solution Explorer mirrors the module structure. Folders: Core, Server, Drivers (with a nested Driver CLIs subfolder), Client, Tooling. - Move every project folder on disk with git mv (history preserved as renames). - Recompute relative paths in 57 .csproj files: cross-category ProjectReferences, the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external mxaccessgw refs in Driver.Galaxy and its test project. - Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders. - Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL, integration, install). Build green (0 errors); unit tests pass. Docs left for a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Security;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Server.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Deterministic guards for Active Directory compatibility of the internal helpers
|
||||
/// <see cref="LdapUserAuthenticator"/> relies on. We can't live-bind against AD in unit
|
||||
/// tests — instead, we pin the behaviors AD depends on (DN-parsing of AD-style
|
||||
/// <c>memberOf</c> values, filter escaping with case-preserving RDN extraction) so a
|
||||
/// future refactor can't silently break the AD path while the GLAuth live-smoke stays
|
||||
/// green.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class LdapUserAuthenticatorAdCompatTests
|
||||
{
|
||||
[Fact]
|
||||
public void ExtractFirstRdnValue_parses_AD_memberOf_group_name_from_CN_dn()
|
||||
{
|
||||
// AD's memberOf values use uppercase CN=… and full domain paths. The extractor
|
||||
// returns the first RDN's value regardless of attribute-type case, so operators'
|
||||
// GroupToRole keys stay readable ("OPCUA-Operators" not "CN=OPCUA-Operators,...").
|
||||
var dn = "CN=OPCUA-Operators,OU=OPC UA Security Groups,OU=Groups,DC=corp,DC=example,DC=com";
|
||||
LdapUserAuthenticator.ExtractFirstRdnValue(dn).ShouldBe("OPCUA-Operators");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractFirstRdnValue_handles_mixed_case_and_spaces_in_group_name()
|
||||
{
|
||||
var dn = "CN=Domain Users,CN=Users,DC=corp,DC=example,DC=com";
|
||||
LdapUserAuthenticator.ExtractFirstRdnValue(dn).ShouldBe("Domain Users");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtractFirstRdnValue_also_works_for_OpenLDAP_ou_style_memberOf()
|
||||
{
|
||||
// GLAuth + some OpenLDAP deployments expose memberOf as ou=<group>,ou=groups,...
|
||||
// The authenticator needs one extractor that tolerates both shapes since directories
|
||||
// in the field mix them depending on schema.
|
||||
var dn = "ou=WriteOperate,ou=groups,dc=lmxopcua,dc=local";
|
||||
LdapUserAuthenticator.ExtractFirstRdnValue(dn).ShouldBe("WriteOperate");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EscapeLdapFilter_prevents_injection_via_samaccountname_lookup()
|
||||
{
|
||||
// AD login names can contain characters that are meaningful to LDAP filter syntax
|
||||
// (parens, backslashes). The authenticator builds filters as
|
||||
// ($"({UserNameAttribute}={EscapeLdapFilter(username)})") so injection attempts must
|
||||
// not break out of the filter. The RFC 4515 escape set is: \ → \5c, * → \2a, ( → \28,
|
||||
// ) → \29, \0 → \00.
|
||||
LdapUserAuthenticator.EscapeLdapFilter("admin)(cn=*")
|
||||
.ShouldBe("admin\\29\\28cn=\\2a");
|
||||
LdapUserAuthenticator.EscapeLdapFilter("domain\\user")
|
||||
.ShouldBe("domain\\5cuser");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapOptions_default_UserNameAttribute_is_uid_for_rfc2307_compat()
|
||||
{
|
||||
// Regression guard: PR 31 introduced UserNameAttribute with a default of "uid" so
|
||||
// existing deployments (pre-AD config) keep working. Changing the default breaks
|
||||
// everyone's config silently; require an explicit review.
|
||||
new LdapOptions().UserNameAttribute.ShouldBe("uid");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user