fix(secrets): SqlServer/Akka extensions take contentRootPath; last explicit root wins (scadaproj#31)
The SQL-Server extensions called the 3-arg AddZbSecrets, and the store-path validator captured the content root of whichever call registered it first. In hub mode no registration order applied the pre-host under-content-root guard: extension-first dropped the app's 4-arg root, core-first lost the replicator (#30). That is the 0.6.0 store-path hole, reopened. - AddZbSecretsSqlServerStore / AddZbSecretsSqlServerReplication / AddZbSecretsAkkaReplication gain a trailing optional contentRootPath, forwarded to the 4-arg AddZbSecrets - the root now lives in one SecretsContentRoot instance holder per service collection; the last explicit root wins, null never erases one; the IHostEnvironment fallback is unchanged - tests: ContentRootTests (hub mode, three compositions, artifact asserted before the throw, positive control), last-explicit-wins theory in SecretsStorePathRulesTests, Akka forwarding test - code-reviews: Secrets-002 Resolved; README regenerated Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+18
-4
@@ -39,16 +39,23 @@ public static class SqlServerSecretsServiceCollectionExtensions
|
||||
/// <param name="config">The application configuration.</param>
|
||||
/// <param name="secretsSectionPath">Configuration section holding the core secrets options.</param>
|
||||
/// <param name="sqlServerSectionPath">Configuration section holding the SQL-Server options.</param>
|
||||
/// <param name="contentRootPath">
|
||||
/// The application content root, forwarded to <c>AddZbSecrets</c> so the store-path guard can
|
||||
/// reject a <c>Secrets:SqlitePath</c> inside it. Pass it from every pre-host composition (a
|
||||
/// container with no <c>IHostEnvironment</c>, such as a <c>${secret:}</c> expander's), exactly as
|
||||
/// for the 4-argument <c>AddZbSecrets</c>. The last explicitly supplied root wins across calls.
|
||||
/// </param>
|
||||
/// <returns>The same <paramref name="services"/> instance, for chaining.</returns>
|
||||
public static IServiceCollection AddZbSecretsSqlServerStore(
|
||||
this IServiceCollection services,
|
||||
IConfiguration config,
|
||||
string secretsSectionPath = "Secrets",
|
||||
string sqlServerSectionPath = "Secrets:SqlServer")
|
||||
string sqlServerSectionPath = "Secrets:SqlServer",
|
||||
string? contentRootPath = null)
|
||||
{
|
||||
RegisterSqlServerCore(services, config, sqlServerSectionPath);
|
||||
|
||||
services.AddZbSecrets(config, secretsSectionPath);
|
||||
services.AddZbSecrets(config, secretsSectionPath, contentRootPath);
|
||||
|
||||
// REPLACE, not TryAdd (scadaproj#30): the application may already have called AddZbSecrets,
|
||||
// whose SQLite ISecretStore / ISecretsStoreMigrator a TryAdd here would silently leave in
|
||||
@@ -81,19 +88,26 @@ public static class SqlServerSecretsServiceCollectionExtensions
|
||||
/// <param name="config">The application configuration.</param>
|
||||
/// <param name="secretsSectionPath">Configuration section holding the core secrets options.</param>
|
||||
/// <param name="sqlServerSectionPath">Configuration section holding the SQL-Server options.</param>
|
||||
/// <param name="contentRootPath">
|
||||
/// The application content root, forwarded to <c>AddZbSecrets</c> so the store-path guard can
|
||||
/// reject a <c>Secrets:SqlitePath</c> inside it. Pass it from every pre-host composition (a
|
||||
/// container with no <c>IHostEnvironment</c>, such as a <c>${secret:}</c> expander's), exactly as
|
||||
/// for the 4-argument <c>AddZbSecrets</c>. The last explicitly supplied root wins across calls.
|
||||
/// </param>
|
||||
/// <returns>The same <paramref name="services"/> instance, for chaining.</returns>
|
||||
public static IServiceCollection AddZbSecretsSqlServerReplication(
|
||||
this IServiceCollection services,
|
||||
IConfiguration config,
|
||||
string secretsSectionPath = "Secrets",
|
||||
string sqlServerSectionPath = "Secrets:SqlServer")
|
||||
string sqlServerSectionPath = "Secrets:SqlServer",
|
||||
string? contentRootPath = null)
|
||||
{
|
||||
RegisterSqlServerCore(services, config, sqlServerSectionPath);
|
||||
|
||||
services.TryAddSingleton<ISecretReplicationHub>(sp => sp.GetRequiredService<SqlServerSecretStore>());
|
||||
|
||||
// The local store + its migrator come from AddZbSecrets (SQLite by default).
|
||||
services.AddZbSecrets(config, secretsSectionPath);
|
||||
services.AddZbSecrets(config, secretsSectionPath, contentRootPath);
|
||||
|
||||
// REPLACE, not TryAdd (scadaproj#30): if the application called AddZbSecrets first, its
|
||||
// NoOpSecretReplicator is already registered and a TryAdd would silently lose to it — live
|
||||
|
||||
Reference in New Issue
Block a user