mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Replace raw environment variable reads with IConfiguration in backend
This commit is contained in:
@@ -32,7 +32,7 @@ public class JwtAuthenticationHandler(
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
// Use the same access-token secret source as TokenService to avoid mismatched validation.
|
||||
string? secret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET");
|
||||
string? secret = configuration["ACCESS_TOKEN_SECRET"];
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
secret = configuration["Jwt:SecretKey"];
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Infrastructure.Jwt;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Reqnroll;
|
||||
|
||||
namespace API.Specs.Steps;
|
||||
@@ -19,11 +21,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
private const string PreviousAccessTokenKey = "previousAccessToken";
|
||||
private const string PreviousRefreshTokenKey = "previousRefreshToken";
|
||||
|
||||
private HttpClient GetClient()
|
||||
private TestApiFactory GetFactory()
|
||||
{
|
||||
if (scenario.TryGetValue<HttpClient>(ClientKey, out HttpClient? client))
|
||||
return client;
|
||||
|
||||
TestApiFactory? factory = scenario.TryGetValue<TestApiFactory>(
|
||||
FactoryKey,
|
||||
out TestApiFactory? f
|
||||
@@ -31,16 +30,28 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
? f
|
||||
: new TestApiFactory();
|
||||
scenario[FactoryKey] = factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
client = factory.CreateClient();
|
||||
private HttpClient GetClient()
|
||||
{
|
||||
if (scenario.TryGetValue<HttpClient>(ClientKey, out HttpClient? client))
|
||||
return client;
|
||||
|
||||
client = GetFactory().CreateClient();
|
||||
scenario[ClientKey] = client;
|
||||
return client;
|
||||
}
|
||||
|
||||
private static string GetRequiredEnvVar(string name)
|
||||
/// <summary>
|
||||
/// Reads a required configuration value from the running <see cref="TestApiFactory" />'s own
|
||||
/// <see cref="IConfiguration" />, so forged tokens are always signed with the exact secret the
|
||||
/// app under test resolved -- rather than assuming this process's environment variables match it.
|
||||
/// </summary>
|
||||
private string GetRequiredConfigValue(string key)
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(name)
|
||||
?? throw new InvalidOperationException($"{name} environment variable is not set");
|
||||
return GetFactory().Services.GetRequiredService<IConfiguration>()[key]
|
||||
?? throw new InvalidOperationException($"{key} configuration value is not set");
|
||||
}
|
||||
|
||||
private static string GenerateJwtToken(
|
||||
@@ -420,7 +431,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredEnvVar("ACCESS_TOKEN_SECRET");
|
||||
string secret = GetRequiredConfigValue("ACCESS_TOKEN_SECRET");
|
||||
scenario["accessToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
@@ -439,7 +450,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredEnvVar("CONFIRMATION_TOKEN_SECRET");
|
||||
string secret = GetRequiredConfigValue("CONFIRMATION_TOKEN_SECRET");
|
||||
scenario["confirmationToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
@@ -458,7 +469,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredEnvVar("CONFIRMATION_TOKEN_SECRET");
|
||||
string secret = GetRequiredConfigValue("CONFIRMATION_TOKEN_SECRET");
|
||||
scenario["confirmationToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dbup" Version="5.0.41" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.Configuration.EnvironmentVariables"
|
||||
Version="9.0.0"
|
||||
/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Sql\Infrastructure.Sql.csproj" />
|
||||
|
||||
@@ -4,13 +4,14 @@ using DbUp;
|
||||
using DbUp.Engine;
|
||||
using Infrastructure.Sql;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Database.Migrations;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the database migration runner. Reads connection details from environment
|
||||
/// variables, optionally drops and recreates the target database, and applies all SQL
|
||||
/// migration scripts embedded in this assembly using DbUp.
|
||||
/// Entry point for the database migration runner. Reads connection details from configuration
|
||||
/// (backed by environment variables), optionally drops and recreates the target database, and
|
||||
/// applies all SQL migration scripts embedded in this assembly using DbUp.
|
||||
/// </summary>
|
||||
public static class Program
|
||||
{
|
||||
@@ -27,7 +28,10 @@ public static class Program
|
||||
|
||||
try
|
||||
{
|
||||
DatabaseSettings settings = DatabaseSettings.FromEnvironment();
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
DatabaseSettings settings = DatabaseSettings.FromConfiguration(configuration);
|
||||
|
||||
if (settings.ClearDatabase)
|
||||
{
|
||||
@@ -159,28 +163,28 @@ public static class Program
|
||||
/// <summary>
|
||||
/// Builds the settings from the <c>DB_SERVER</c>, <c>DB_NAME</c>, <c>DB_USER</c>,
|
||||
/// <c>DB_PASSWORD</c>, <c>DB_TRUST_SERVER_CERTIFICATE</c>, and <c>CLEAR_DATABASE</c>
|
||||
/// environment variables.
|
||||
/// configuration values.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when a required environment variable is not set.
|
||||
/// Thrown when a required configuration value is not set.
|
||||
/// </exception>
|
||||
public static DatabaseSettings FromEnvironment()
|
||||
public static DatabaseSettings FromConfiguration(IConfiguration configuration)
|
||||
{
|
||||
string databaseName =
|
||||
Environment.GetEnvironmentVariable("DB_NAME")
|
||||
configuration["DB_NAME"]
|
||||
?? throw new InvalidOperationException(
|
||||
"The DB_NAME environment variable is not set."
|
||||
);
|
||||
|
||||
bool clearDatabase = string.Equals(
|
||||
Environment.GetEnvironmentVariable("CLEAR_DATABASE"),
|
||||
configuration["CLEAR_DATABASE"],
|
||||
"true",
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
|
||||
return new DatabaseSettings(
|
||||
SqlConnectionStringHelper.BuildConnectionString(databaseName),
|
||||
SqlConnectionStringHelper.BuildMasterConnectionString(),
|
||||
SqlConnectionStringHelper.BuildConnectionString(configuration, databaseName),
|
||||
SqlConnectionStringHelper.BuildMasterConnectionString(configuration),
|
||||
databaseName,
|
||||
clearDatabase
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#region
|
||||
|
||||
using Infrastructure.Sql;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -8,7 +9,9 @@ namespace Database.Seed.DatabaseHelpers;
|
||||
|
||||
public class ConnectionStrings
|
||||
{
|
||||
public static string SqlServerConnectionString => SqlConnectionStringHelper.BuildConnectionString();
|
||||
public static string GetSqlServerConnectionString(IConfiguration configuration) =>
|
||||
SqlConnectionStringHelper.BuildConnectionString(configuration);
|
||||
|
||||
public static string SqliteConnectionString =>
|
||||
"Data Source=SeedData/biergarten_seed_2026-07-20T05-46-02.993511Z.sqlite";
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ static async Task<int> RunAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
ServiceCollection services = [];
|
||||
services.AddSingleton<IConfiguration>(
|
||||
new ConfigurationBuilder().Build());
|
||||
services.AddSingleton(configuration);
|
||||
services.AddInfrastructureSql();
|
||||
services.AddFeaturesBreweries();
|
||||
services.AddFeaturesLocations();
|
||||
|
||||
@@ -7,6 +7,7 @@ using Features.Auth.Tests.TestSupport;
|
||||
using FluentAssertions;
|
||||
using Infrastructure.Jwt;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Moq;
|
||||
|
||||
namespace Features.Auth.Tests.Services;
|
||||
@@ -22,21 +23,23 @@ public class TokenServiceRefreshTests
|
||||
_tokenInfraMock = new Mock<ITokenInfrastructure>();
|
||||
_userManagerMock = UserManagerMockFactory.Create();
|
||||
|
||||
// Set environment variables for tokens
|
||||
Environment.SetEnvironmentVariable(
|
||||
"ACCESS_TOKEN_SECRET",
|
||||
"test-access-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
Environment.SetEnvironmentVariable(
|
||||
"REFRESH_TOKEN_SECRET",
|
||||
"test-refresh-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
Environment.SetEnvironmentVariable(
|
||||
"CONFIRMATION_TOKEN_SECRET",
|
||||
"test-confirmation-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["ACCESS_TOKEN_SECRET"] = "test-access-secret-that-is-very-long-1234567890",
|
||||
["REFRESH_TOKEN_SECRET"] = "test-refresh-secret-that-is-very-long-1234567890",
|
||||
["CONFIRMATION_TOKEN_SECRET"] =
|
||||
"test-confirmation-secret-that-is-very-long-1234567890",
|
||||
}
|
||||
)
|
||||
.Build();
|
||||
|
||||
_tokenService = new TokenService(_tokenInfraMock.Object, _userManagerMock.Object);
|
||||
_tokenService = new TokenService(
|
||||
_tokenInfraMock.Object,
|
||||
_userManagerMock.Object,
|
||||
configuration
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -5,6 +5,7 @@ using Features.Auth.Services;
|
||||
using Features.Auth.Tests.TestSupport;
|
||||
using FluentAssertions;
|
||||
using Infrastructure.Jwt;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Moq;
|
||||
|
||||
namespace Features.Auth.Tests.Services;
|
||||
@@ -18,23 +19,22 @@ public class TokenServiceValidationTests
|
||||
{
|
||||
_tokenInfraMock = new Mock<ITokenInfrastructure>();
|
||||
|
||||
// Set environment variables for tokens
|
||||
Environment.SetEnvironmentVariable(
|
||||
"ACCESS_TOKEN_SECRET",
|
||||
"test-access-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
Environment.SetEnvironmentVariable(
|
||||
"REFRESH_TOKEN_SECRET",
|
||||
"test-refresh-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
Environment.SetEnvironmentVariable(
|
||||
"CONFIRMATION_TOKEN_SECRET",
|
||||
"test-confirmation-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["ACCESS_TOKEN_SECRET"] = "test-access-secret-that-is-very-long-1234567890",
|
||||
["REFRESH_TOKEN_SECRET"] = "test-refresh-secret-that-is-very-long-1234567890",
|
||||
["CONFIRMATION_TOKEN_SECRET"] =
|
||||
"test-confirmation-secret-that-is-very-long-1234567890",
|
||||
}
|
||||
)
|
||||
.Build();
|
||||
|
||||
_tokenService = new TokenService(
|
||||
_tokenInfraMock.Object,
|
||||
UserManagerMockFactory.Create().Object
|
||||
UserManagerMockFactory.Create().Object,
|
||||
configuration
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ using Domain.Exceptions;
|
||||
using Features.Auth.Identity;
|
||||
using Infrastructure.Jwt;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Features.Auth.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="ITokenService" /> that generates and validates JWTs
|
||||
/// for access, refresh, and confirmation flows using secrets read from environment variables.
|
||||
/// for access, refresh, and confirmation flows using secrets read from configuration.
|
||||
/// </summary>
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
@@ -22,30 +23,31 @@ public class TokenService : ITokenService
|
||||
/// <summary>Initializes a new instance of the <see cref="TokenService" /> class.</summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when any of the <c>ACCESS_TOKEN_SECRET</c>, <c>REFRESH_TOKEN_SECRET</c>, or
|
||||
/// <c>CONFIRMATION_TOKEN_SECRET</c> environment variables are not set.
|
||||
/// <c>CONFIRMATION_TOKEN_SECRET</c> configuration values are not set.
|
||||
/// </exception>
|
||||
public TokenService(
|
||||
ITokenInfrastructure tokenInfrastructure,
|
||||
UserManager<ApplicationUser> userManager
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IConfiguration configuration
|
||||
)
|
||||
{
|
||||
_tokenInfrastructure = tokenInfrastructure;
|
||||
_userManager = userManager;
|
||||
|
||||
_accessTokenSecret =
|
||||
Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET")
|
||||
configuration["ACCESS_TOKEN_SECRET"]
|
||||
?? throw new InvalidOperationException(
|
||||
"ACCESS_TOKEN_SECRET environment variable is not set"
|
||||
);
|
||||
|
||||
_refreshTokenSecret =
|
||||
Environment.GetEnvironmentVariable("REFRESH_TOKEN_SECRET")
|
||||
configuration["REFRESH_TOKEN_SECRET"]
|
||||
?? throw new InvalidOperationException(
|
||||
"REFRESH_TOKEN_SECRET environment variable is not set"
|
||||
);
|
||||
|
||||
_confirmationTokenSecret =
|
||||
Environment.GetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET")
|
||||
configuration["CONFIRMATION_TOKEN_SECRET"]
|
||||
?? throw new InvalidOperationException(
|
||||
"CONFIRMATION_TOKEN_SECRET environment variable is not set"
|
||||
);
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
<RootNamespace>Features.Emails</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.Configuration.Abstractions"
|
||||
Version="10.0.1"
|
||||
/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Email\Infrastructure.Email.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Email.Templates\Infrastructure.Email.Templates.csproj" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Infrastructure.Email;
|
||||
using Infrastructure.Email.Templates.Rendering;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Features.Emails.Services;
|
||||
|
||||
@@ -8,18 +9,19 @@ namespace Features.Emails.Services;
|
||||
/// </summary>
|
||||
public class EmailDispatcher(
|
||||
IEmailProvider emailProvider,
|
||||
IEmailTemplateProvider emailTemplateProvider
|
||||
IEmailTemplateProvider emailTemplateProvider,
|
||||
IConfiguration configuration
|
||||
) : IEmailDispatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The base URL of the website, used to build confirmation links. Read from the
|
||||
/// <c>WEBSITE_BASE_URL</c> environment variable.
|
||||
/// <c>WEBSITE_BASE_URL</c> configuration value.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown at type initialization time when the <c>WEBSITE_BASE_URL</c> environment variable is not set.
|
||||
/// Thrown when the <c>WEBSITE_BASE_URL</c> configuration value is not set.
|
||||
/// </exception>
|
||||
private static readonly string WebsiteBaseUrl =
|
||||
Environment.GetEnvironmentVariable("WEBSITE_BASE_URL")
|
||||
private string WebsiteBaseUrl =>
|
||||
configuration["WEBSITE_BASE_URL"]
|
||||
?? throw new InvalidOperationException("WEBSITE_BASE_URL environment variable is not set");
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference
|
||||
Include="Microsoft.Extensions.Configuration.Abstractions"
|
||||
Version="10.0.1"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MimeKit;
|
||||
|
||||
namespace Infrastructure.Email;
|
||||
|
||||
/// <summary>
|
||||
/// SMTP email service implementation using MailKit.
|
||||
/// Configured via environment variables.
|
||||
/// Configured via application configuration (backed by environment variables).
|
||||
/// </summary>
|
||||
public class SmtpEmailProvider : IEmailProvider
|
||||
{
|
||||
@@ -20,35 +21,35 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SmtpEmailProvider" /> class, reading SMTP configuration
|
||||
/// from environment variables (<c>SMTP_HOST</c>, <c>SMTP_PORT</c>, <c>SMTP_USERNAME</c>,
|
||||
/// <c>SMTP_PASSWORD</c>, <c>SMTP_USE_SSL</c>, <c>SMTP_FROM_EMAIL</c>, <c>SMTP_FROM_NAME</c>).
|
||||
/// (<c>SMTP_HOST</c>, <c>SMTP_PORT</c>, <c>SMTP_USERNAME</c>, <c>SMTP_PASSWORD</c>, <c>SMTP_USE_SSL</c>,
|
||||
/// <c>SMTP_FROM_EMAIL</c>, <c>SMTP_FROM_NAME</c>) from <paramref name="configuration" />.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when <c>SMTP_HOST</c> or <c>SMTP_FROM_EMAIL</c> is not set, or when <c>SMTP_PORT</c> is not a valid integer.
|
||||
/// </exception>
|
||||
public SmtpEmailProvider()
|
||||
public SmtpEmailProvider(IConfiguration configuration)
|
||||
{
|
||||
_host =
|
||||
Environment.GetEnvironmentVariable("SMTP_HOST")
|
||||
configuration["SMTP_HOST"]
|
||||
?? throw new InvalidOperationException("SMTP_HOST environment variable is not set");
|
||||
|
||||
string portString = Environment.GetEnvironmentVariable("SMTP_PORT") ?? "587";
|
||||
string portString = configuration["SMTP_PORT"] ?? "587";
|
||||
if (!int.TryParse(portString, out _port))
|
||||
throw new InvalidOperationException($"SMTP_PORT '{portString}' is not a valid integer");
|
||||
|
||||
_username = Environment.GetEnvironmentVariable("SMTP_USERNAME");
|
||||
_password = Environment.GetEnvironmentVariable("SMTP_PASSWORD");
|
||||
_username = configuration["SMTP_USERNAME"];
|
||||
_password = configuration["SMTP_PASSWORD"];
|
||||
|
||||
string useSslString = Environment.GetEnvironmentVariable("SMTP_USE_SSL") ?? "true";
|
||||
string useSslString = configuration["SMTP_USE_SSL"] ?? "true";
|
||||
_useSsl = bool.Parse(useSslString);
|
||||
|
||||
_fromEmail =
|
||||
Environment.GetEnvironmentVariable("SMTP_FROM_EMAIL")
|
||||
configuration["SMTP_FROM_EMAIL"]
|
||||
?? throw new InvalidOperationException(
|
||||
"SMTP_FROM_EMAIL environment variable is not set"
|
||||
);
|
||||
|
||||
_fromName = Environment.GetEnvironmentVariable("SMTP_FROM_NAME") ?? "The Biergarten";
|
||||
_fromName = configuration["SMTP_FROM_NAME"] ?? "The Biergarten";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -20,21 +20,21 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration) : ISqlCon
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the SQL Server connection string, preferring (in order): the <c>DB_CONNECTION_STRING</c>
|
||||
/// environment variable, a connection string built from individual <c>DB_*</c> environment variables,
|
||||
/// configuration value, a connection string built from individual <c>DB_*</c> configuration values,
|
||||
/// and finally the <c>"Default"</c> connection string from configuration.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when no connection string can be resolved from any source.</exception>
|
||||
private static string GetConnectionString(IConfiguration configuration)
|
||||
{
|
||||
// Check for full connection string first
|
||||
string? fullConnectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
|
||||
string? fullConnectionString = configuration["DB_CONNECTION_STRING"];
|
||||
if (!string.IsNullOrEmpty(fullConnectionString))
|
||||
return fullConnectionString;
|
||||
|
||||
// Try to build from individual environment variables (preferred method for Docker)
|
||||
// Try to build from individual configuration values (preferred method for Docker)
|
||||
try
|
||||
{
|
||||
return SqlConnectionStringHelper.BuildConnectionString();
|
||||
return SqlConnectionStringHelper.BuildConnectionString(configuration);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Infrastructure.Sql;
|
||||
|
||||
/// <summary>
|
||||
/// Helper for building SQL Server connection strings from environment variables.
|
||||
/// Helper for building SQL Server connection strings from configuration
|
||||
/// (backed by the <c>DB_*</c> environment variables).
|
||||
/// </summary>
|
||||
public static class SqlConnectionStringHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a SQL Server connection string from environment variables.
|
||||
/// Builds a SQL Server connection string from configuration.
|
||||
/// </summary>
|
||||
/// <remarks>Throws <see cref="InvalidOperationException" /> if DB_SERVER, DB_NAME, DB_USER, or DB_PASSWORD is unset.</remarks>
|
||||
/// <param name="databaseName">If null, falls back to the DB_NAME environment variable.</param>
|
||||
public static string BuildConnectionString(string? databaseName = null)
|
||||
/// <param name="configuration">The configuration to read the <c>DB_*</c> values from.</param>
|
||||
/// <param name="databaseName">If null, falls back to the DB_NAME configuration value.</param>
|
||||
public static string BuildConnectionString(
|
||||
IConfiguration configuration,
|
||||
string? databaseName = null
|
||||
)
|
||||
{
|
||||
string server = Required("DB_SERVER");
|
||||
string dbName = databaseName ?? Required("DB_NAME");
|
||||
string user = Required("DB_USER");
|
||||
string password = Required("DB_PASSWORD");
|
||||
string server = Required(configuration, "DB_SERVER");
|
||||
string dbName = databaseName ?? Required(configuration, "DB_NAME");
|
||||
string user = Required(configuration, "DB_USER");
|
||||
string password = Required(configuration, "DB_PASSWORD");
|
||||
|
||||
SqlConnectionStringBuilder builder = new()
|
||||
{
|
||||
@@ -25,7 +31,7 @@ public static class SqlConnectionStringHelper
|
||||
InitialCatalog = dbName,
|
||||
UserID = user,
|
||||
Password = password,
|
||||
TrustServerCertificate = ResolveTrustServerCertificate(),
|
||||
TrustServerCertificate = ResolveTrustServerCertificate(configuration),
|
||||
Encrypt = true,
|
||||
};
|
||||
|
||||
@@ -33,29 +39,27 @@ public static class SqlConnectionStringHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a connection string to the master database using environment variables.
|
||||
/// Builds a connection string to the master database using configuration.
|
||||
/// </summary>
|
||||
public static string BuildMasterConnectionString()
|
||||
public static string BuildMasterConnectionString(IConfiguration configuration)
|
||||
{
|
||||
return BuildConnectionString("master");
|
||||
return BuildConnectionString(configuration, "master");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the <c>DB_TRUST_SERVER_CERTIFICATE</c> environment variable, defaulting to
|
||||
/// Resolves the <c>DB_TRUST_SERVER_CERTIFICATE</c> configuration value, defaulting to
|
||||
/// <c>true</c> when it is unset or not a valid boolean.
|
||||
/// </summary>
|
||||
private static bool ResolveTrustServerCertificate()
|
||||
private static bool ResolveTrustServerCertificate(IConfiguration configuration)
|
||||
{
|
||||
return !bool.TryParse(
|
||||
Environment.GetEnvironmentVariable("DB_TRUST_SERVER_CERTIFICATE"),
|
||||
configuration["DB_TRUST_SERVER_CERTIFICATE"],
|
||||
out bool trustServerCertificate
|
||||
)
|
||||
|| trustServerCertificate;
|
||||
}
|
||||
|
||||
private static string Required(string variableName) =>
|
||||
Environment.GetEnvironmentVariable(variableName)
|
||||
?? throw new InvalidOperationException(
|
||||
$"The {variableName} environment variable is not set."
|
||||
);
|
||||
private static string Required(IConfiguration configuration, string key) =>
|
||||
configuration[key]
|
||||
?? throw new InvalidOperationException($"The {key} environment variable is not set.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user