namespace ZB.MOM.WW.Auth.ApiKeys.SqlServer; /// /// Options for the shared SQL Server API-key store (section supplied by the host, e.g. /// ScadaBridge:InboundApi:ApiKeyStore:SqlServer). /// public sealed class SqlServerApiKeyOptions { /// /// Full SQL Server connection string. Required. Never a ${secret:} reference on hosts /// whose secret store is itself SQL-backed. /// public string ConnectionString { get; set; } = string.Empty; /// Schema that owns the three tables. Letters, digits and underscore only (it is interpolated into DDL). public string Schema { get; set; } = "zbauth"; /// /// Command timeout applied to every store operation. Defaults to 30 seconds — long enough for /// a contended shared database, short enough that a hung server surfaces as an error rather /// than stalling a resolve indefinitely. /// public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromSeconds(30); /// Fails fast on a blank connection string, an unsafe schema identifier, or a non-positive command timeout. /// The options are not usable. public void Validate() { if (string.IsNullOrWhiteSpace(ConnectionString)) throw new InvalidOperationException("SQL Server API-key store requires a ConnectionString."); ValidateIdentifier(Schema); if (CommandTimeout <= TimeSpan.Zero) throw new InvalidOperationException($"SQL Server API-key store CommandTimeout must be positive (was {CommandTimeout})."); } /// /// Strict allow-list for a SQL identifier that will be interpolated into DDL or query text. /// Rejects everything that is not [A-Za-z_][A-Za-z0-9_]{0,63} — no brackets, quotes, dots /// or spaces — so no bracket-escape (]]) trick can break out of the quoting applied at the /// use site. Uses the ASCII-only character tests deliberately, so homoglyph and full-width /// look-alikes are rejected rather than accepted as "letters". /// /// The candidate identifier. /// The same value, once validated. /// The value is not a plain identifier. /// /// Public because is public and must /// defend itself rather than trust that every caller routed through . /// public static string ValidateIdentifier(string schemaName) { if (string.IsNullOrEmpty(schemaName) || schemaName.Length > 64 || (!char.IsAsciiLetter(schemaName[0]) && schemaName[0] != '_') || !schemaName.All(c => char.IsAsciiLetterOrDigit(c) || c == '_')) { throw new InvalidOperationException( $"SQL Server API-key store schema name '{schemaName}' is not a safe identifier."); } return schemaName; } }