feat(auth): cut ScadaBridge over to ZB.MOM.WW.Auth.Ldap; nest+rename Ldap config; roles+sitescope via IGroupRoleMapper (Task 1.2/1.4)
This commit is contained in:
@@ -43,11 +43,15 @@ public class CentralActorPathTests : IAsyncLifetime
|
||||
["ScadaBridge:Cluster:MinNrOfMembers"] = "1",
|
||||
["ScadaBridge:Database:SkipMigrations"] = "true",
|
||||
["ScadaBridge:Security:JwtSigningKey"] = "test-signing-key-must-be-at-least-32-chars-long!",
|
||||
["ScadaBridge:Security:LdapServer"] = "localhost",
|
||||
["ScadaBridge:Security:LdapPort"] = "3893",
|
||||
["ScadaBridge:Security:LdapUseTls"] = "false",
|
||||
["ScadaBridge:Security:AllowInsecureLdap"] = "true",
|
||||
["ScadaBridge:Security:LdapSearchBase"] = "dc=scadabridge,dc=local",
|
||||
// Task 1.4: LDAP settings nest under Security:Ldap (shared LdapOptions).
|
||||
// ServiceAccountDn is now required by the library's LdapOptionsValidator
|
||||
// (ValidateOnStart), so it must be present for the host to start.
|
||||
["ScadaBridge:Security:Ldap:Server"] = "localhost",
|
||||
["ScadaBridge:Security:Ldap:Port"] = "3893",
|
||||
["ScadaBridge:Security:Ldap:Transport"] = "None",
|
||||
["ScadaBridge:Security:Ldap:AllowInsecure"] = "true",
|
||||
["ScadaBridge:Security:Ldap:SearchBase"] = "dc=scadabridge,dc=local",
|
||||
["ScadaBridge:Security:Ldap:ServiceAccountDn"] = "cn=admin,dc=scadabridge,dc=local",
|
||||
});
|
||||
});
|
||||
builder.UseSetting("ScadaBridge:Node:Role", "Central");
|
||||
|
||||
@@ -108,11 +108,15 @@ public class CentralAuditWiringTests : IDisposable
|
||||
["ScadaBridge:Cluster:SeedNodes:1"] = "akka.tcp://scadabridge@localhost:2552",
|
||||
["ScadaBridge:Database:SkipMigrations"] = "true",
|
||||
["ScadaBridge:Security:JwtSigningKey"] = "test-signing-key-must-be-at-least-32-chars-long!",
|
||||
["ScadaBridge:Security:LdapServer"] = "localhost",
|
||||
["ScadaBridge:Security:LdapPort"] = "3893",
|
||||
["ScadaBridge:Security:LdapUseTls"] = "false",
|
||||
["ScadaBridge:Security:AllowInsecureLdap"] = "true",
|
||||
["ScadaBridge:Security:LdapSearchBase"] = "dc=scadabridge,dc=local",
|
||||
// Task 1.4: LDAP settings nest under Security:Ldap (shared LdapOptions).
|
||||
// ServiceAccountDn is now required by the library's LdapOptionsValidator
|
||||
// (ValidateOnStart), so it must be present for the host to start.
|
||||
["ScadaBridge:Security:Ldap:Server"] = "localhost",
|
||||
["ScadaBridge:Security:Ldap:Port"] = "3893",
|
||||
["ScadaBridge:Security:Ldap:Transport"] = "None",
|
||||
["ScadaBridge:Security:Ldap:AllowInsecure"] = "true",
|
||||
["ScadaBridge:Security:Ldap:SearchBase"] = "dc=scadabridge,dc=local",
|
||||
["ScadaBridge:Security:Ldap:ServiceAccountDn"] = "cn=admin,dc=scadabridge,dc=local",
|
||||
["ScadaBridge:InboundApi:ApiKeyPepper"] = "test-inbound-api-key-pepper-at-least-32-chars!",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ using ZB.MOM.WW.ScadaBridge.Host.Health;
|
||||
using ZB.MOM.WW.ScadaBridge.InboundAPI;
|
||||
using ZB.MOM.WW.ScadaBridge.ManagementService;
|
||||
using ZB.MOM.WW.ScadaBridge.NotificationService;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
using ZB.MOM.WW.ScadaBridge.SiteEventLogging;
|
||||
using ZB.MOM.WW.ScadaBridge.Communication.Grpc;
|
||||
@@ -102,11 +103,15 @@ public class CentralCompositionRootTests : IDisposable
|
||||
["ScadaBridge:Cluster:SeedNodes:1"] = "akka.tcp://scadabridge@localhost:2552",
|
||||
["ScadaBridge:Database:SkipMigrations"] = "true",
|
||||
["ScadaBridge:Security:JwtSigningKey"] = "test-signing-key-must-be-at-least-32-chars-long!",
|
||||
["ScadaBridge:Security:LdapServer"] = "localhost",
|
||||
["ScadaBridge:Security:LdapPort"] = "3893",
|
||||
["ScadaBridge:Security:LdapUseTls"] = "false",
|
||||
["ScadaBridge:Security:AllowInsecureLdap"] = "true",
|
||||
["ScadaBridge:Security:LdapSearchBase"] = "dc=scadabridge,dc=local",
|
||||
// Task 1.4: LDAP settings nest under Security:Ldap (shared LdapOptions).
|
||||
// ServiceAccountDn is now required by the library's LdapOptionsValidator
|
||||
// (ValidateOnStart), so it must be present for the host to start.
|
||||
["ScadaBridge:Security:Ldap:Server"] = "localhost",
|
||||
["ScadaBridge:Security:Ldap:Port"] = "3893",
|
||||
["ScadaBridge:Security:Ldap:Transport"] = "None",
|
||||
["ScadaBridge:Security:Ldap:AllowInsecure"] = "true",
|
||||
["ScadaBridge:Security:Ldap:SearchBase"] = "dc=scadabridge,dc=local",
|
||||
["ScadaBridge:Security:Ldap:ServiceAccountDn"] = "cn=admin,dc=scadabridge,dc=local",
|
||||
// ConfigurationDatabase-012: inbound-API keys are hashed
|
||||
// with a server-side HMAC pepper; ApiKeyHasher fails fast
|
||||
// if it is missing or weak, so resolving ApiKeyValidator
|
||||
@@ -167,6 +172,9 @@ public class CentralCompositionRootTests : IDisposable
|
||||
new object[] { typeof(OperationLockManager) },
|
||||
new object[] { typeof(OAuth2TokenService) },
|
||||
new object[] { typeof(InboundScriptExecutor) },
|
||||
// Security: the shared ZB.MOM.WW.Auth.Ldap service is registered as a stateless
|
||||
// singleton by AddZbLdapAuth (Task 1.2), replacing the old scoped LdapAuthService.
|
||||
new object[] { typeof(ILdapAuthService) },
|
||||
};
|
||||
|
||||
// --- Scoped services ---
|
||||
@@ -193,8 +201,7 @@ public class CentralCompositionRootTests : IDisposable
|
||||
new object[] { typeof(IFlatteningPipeline) },
|
||||
new object[] { typeof(DeploymentService) },
|
||||
new object[] { typeof(ArtifactDeploymentService) },
|
||||
// Security
|
||||
new object[] { typeof(LdapAuthService) },
|
||||
// Security (ILdapAuthService is now a singleton — see CentralSingletonServices)
|
||||
new object[] { typeof(JwtTokenService) },
|
||||
new object[] { typeof(RoleMapper) },
|
||||
// InboundAPI
|
||||
|
||||
@@ -20,7 +20,7 @@ public class StartupValidatorTests
|
||||
["ScadaBridge:Node:NodeHostname"] = "central-node1",
|
||||
["ScadaBridge:Node:RemotingPort"] = "8081",
|
||||
["ScadaBridge:Database:ConfigurationDb"] = "Server=localhost;Database=Config;",
|
||||
["ScadaBridge:Security:LdapServer"] = "ldap.example.com",
|
||||
["ScadaBridge:Security:Ldap:Server"] = "ldap.example.com",
|
||||
["ScadaBridge:Security:JwtSigningKey"] = "test-signing-key-at-least-32-chars-long",
|
||||
["ScadaBridge:Cluster:SeedNodes:0"] = "akka.tcp://scadabridge@central-node1:8081",
|
||||
["ScadaBridge:Cluster:SeedNodes:1"] = "akka.tcp://scadabridge@central-node2:8081",
|
||||
@@ -166,12 +166,14 @@ public class StartupValidatorTests
|
||||
[Fact]
|
||||
public void Central_MissingLdapServer_FailsValidation()
|
||||
{
|
||||
// Task 1.4: the LDAP server key nests under Security:Ldap now. The pre-host
|
||||
// preflight validates the nested key and still fails fast for Central.
|
||||
var values = ValidCentralConfig();
|
||||
values.Remove("ScadaBridge:Security:LdapServer");
|
||||
values.Remove("ScadaBridge:Security:Ldap:Server");
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => StartupValidator.Validate(config));
|
||||
Assert.Contains("LdapServer required for Central", ex.Message);
|
||||
Assert.Contains("Ldap:Server required for Central", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user