using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using ZB.MOM.WW.Secrets.DependencyInjection; using ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.DependencyInjection; namespace ZB.MOM.WW.OtOpcUa.Host.Configuration; /// /// Single registration point for the secrets subsystem on every OtOpcUa node, admin- and /// driver-role alike. Exists as a named extension rather than an inline AddZbSecrets call /// in Program.cs so the choice of ISecretStore implementation is covered by a DI /// resolution test — Program.cs is top-level statements and cannot be exercised directly, /// which is exactly how a "registered but never resolvable" wiring defect ships unnoticed. /// public static class SecretsRegistration { /// Configuration key gating cluster secret replication. Absent or false = off. public const string ReplicationEnabledKey = "Secrets:Replication:Enabled"; /// Configuration section holding AkkaSecretsReplicationOptions. public const string ReplicationSectionPath = "Secrets:Replication"; /// Configuration section holding the core secrets options. public const string SecretsSectionPath = "Secrets"; /// /// True when cluster secret replication is switched on in configuration. Defaults to false — /// absent configuration must mean "off". /// /// The application configuration. /// true when replication is enabled. public static bool IsReplicationEnabled(IConfiguration configuration) { ArgumentNullException.ThrowIfNull(configuration); return configuration.GetValue(ReplicationEnabledKey, defaultValue: false); } /// /// Registers the secrets subsystem. Replication is opt-in: unless /// is true this is exactly the plain local SQLite wiring /// the host has always used. /// /// /// /// The gate is deliberately default-deny. This call decides which ISecretStore the /// container hands to every node, including driver-role nodes with no auth or /// AdminUI surface, where a bad store surfaces as drivers failing to open sessions rather /// than as a failing test. Enabling replication by default would change secret resolution /// across a production fleet with nobody having asked for it. /// /// /// AddZbSecretsAkkaReplication calls AddZbSecrets internally, so the two /// branches are alternatives, never additive — calling both would double-register. /// /// /// The service collection to add to. /// The application configuration. /// The same instance, for chaining. public static IServiceCollection AddOtOpcUaSecrets( this IServiceCollection services, IConfiguration configuration) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); if (!IsReplicationEnabled(configuration)) { services.AddZbSecrets(configuration, SecretsSectionPath); return services; } services.AddZbSecretsAkkaReplication(configuration, SecretsSectionPath, ReplicationSectionPath); // Anti-entropy participation must not hinge on this node happening to touch a secret — see // SecretReplicationStarter for why the library's lazy actor creation is insufficient here. services.AddSingleton(); services.AddHostedService(sp => sp.GetRequiredService()); return services; } }