Add LDAP authentication with role-based OPC UA permissions
Replace static user list with GLAuth LDAP authentication. Group membership (ReadOnly, ReadWrite, AlarmAck) maps to granular OPC UA permissions for write and alarm-ack operations. Anonymous can still browse and read but not write. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,5 +70,224 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Authentication
|
||||
config.AnonymousCanWrite.ShouldBeTrue();
|
||||
config.Users.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthenticationConfiguration_LdapDefaults()
|
||||
{
|
||||
var config = new AuthenticationConfiguration();
|
||||
|
||||
config.Ldap.ShouldNotBeNull();
|
||||
config.Ldap.Enabled.ShouldBeFalse();
|
||||
config.Ldap.Host.ShouldBe("localhost");
|
||||
config.Ldap.Port.ShouldBe(3893);
|
||||
config.Ldap.BaseDN.ShouldBe("dc=lmxopcua,dc=local");
|
||||
config.Ldap.ReadOnlyGroup.ShouldBe("ReadOnly");
|
||||
config.Ldap.ReadWriteGroup.ShouldBe("ReadWrite");
|
||||
config.Ldap.AlarmAckGroup.ShouldBe("AlarmAck");
|
||||
config.Ldap.TimeoutSeconds.ShouldBe(5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapConfiguration_BindDnTemplate_Default()
|
||||
{
|
||||
var config = new LdapConfiguration();
|
||||
config.BindDnTemplate.ShouldBe("cn={username},dc=lmxopcua,dc=local");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ValidBind_ReturnsTrue()
|
||||
{
|
||||
// This test requires GLAuth running on localhost:3893
|
||||
// Skip if not available
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("readonly", "readonly123").ShouldBeTrue();
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// GLAuth not running - skip gracefully
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_InvalidPassword_ReturnsFalse()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("readonly", "wrongpassword").ShouldBeFalse();
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_UnknownUser_ReturnsFalse()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("nonexistent", "anything").ShouldBeFalse();
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ReadOnlyUser_HasReadOnlyRole()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("readonly", "readonly123").ShouldBeTrue();
|
||||
var roles = provider.GetUserRoles("readonly");
|
||||
roles.ShouldContain("ReadOnly");
|
||||
roles.ShouldNotContain("ReadWrite");
|
||||
roles.ShouldNotContain("AlarmAck");
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ReadWriteUser_HasReadWriteRole()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("readwrite", "readwrite123").ShouldBeTrue();
|
||||
var roles = provider.GetUserRoles("readwrite");
|
||||
roles.ShouldContain("ReadWrite");
|
||||
roles.ShouldNotContain("AlarmAck");
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_AlarmAckUser_HasAlarmAckRole()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("alarmack", "alarmack123").ShouldBeTrue();
|
||||
var roles = provider.GetUserRoles("alarmack");
|
||||
roles.ShouldContain("AlarmAck");
|
||||
roles.ShouldNotContain("ReadWrite");
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_AdminUser_HasAllRoles()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
try
|
||||
{
|
||||
provider.ValidateCredentials("admin", "admin123").ShouldBeTrue();
|
||||
var roles = provider.GetUserRoles("admin");
|
||||
roles.ShouldContain("ReadOnly");
|
||||
roles.ShouldContain("ReadWrite");
|
||||
roles.ShouldContain("AlarmAck");
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return; // GLAuth not running
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ImplementsIRoleProvider()
|
||||
{
|
||||
var ldapConfig = CreateGlAuthConfig();
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
(provider is IRoleProvider).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigUserAuthenticationProvider_DoesNotImplementIRoleProvider()
|
||||
{
|
||||
var provider = new ConfigUserAuthenticationProvider(new List<UserCredential>());
|
||||
(provider is IRoleProvider).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ConnectionFailure_ReturnsFalse()
|
||||
{
|
||||
var ldapConfig = new LdapConfiguration
|
||||
{
|
||||
Enabled = true,
|
||||
Host = "localhost",
|
||||
Port = 19999, // no server here
|
||||
TimeoutSeconds = 1
|
||||
};
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
provider.ValidateCredentials("anyone", "anything").ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LdapAuthenticationProvider_ConnectionFailure_GetUserRoles_FallsBackToReadOnly()
|
||||
{
|
||||
var ldapConfig = new LdapConfiguration
|
||||
{
|
||||
Enabled = true,
|
||||
Host = "localhost",
|
||||
Port = 19999, // no server here
|
||||
TimeoutSeconds = 1,
|
||||
ServiceAccountDn = "cn=svc,dc=test",
|
||||
ServiceAccountPassword = "test"
|
||||
};
|
||||
var provider = new LdapAuthenticationProvider(ldapConfig);
|
||||
|
||||
var roles = provider.GetUserRoles("anyone");
|
||||
roles.ShouldContain("ReadOnly");
|
||||
}
|
||||
|
||||
private static LdapConfiguration CreateGlAuthConfig()
|
||||
{
|
||||
return new LdapConfiguration
|
||||
{
|
||||
Enabled = true,
|
||||
Host = "localhost",
|
||||
Port = 3893,
|
||||
BaseDN = "dc=lmxopcua,dc=local",
|
||||
BindDnTemplate = "cn={username},dc=lmxopcua,dc=local",
|
||||
ServiceAccountDn = "cn=serviceaccount,dc=lmxopcua,dc=local",
|
||||
ServiceAccountPassword = "serviceaccount123",
|
||||
TimeoutSeconds = 5,
|
||||
ReadOnlyGroup = "ReadOnly",
|
||||
ReadWriteGroup = "ReadWrite",
|
||||
AlarmAckGroup = "AlarmAck"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user