stalwart-simplelogin-middle.../StalwartSimpleLoginMiddleware/Program.cs
2025-05-10 05:25:22 -04:00

116 lines
No EOL
3.8 KiB
C#

using System.Text.Json.Serialization;
using AspNetCore.Authentication.ApiKey;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.OpenApi.Models;
using Npgsql;
using StalwartSimpleLoginMiddleware.Contexts;
using StalwartSimpleLoginMiddleware.Models;
using StalwartSimpleLoginMiddleware.Repositories;
using StalwartSimpleLoginMiddleware.Services;
var logger = LoggerFactory.Create(logging => logging.AddConsole()).CreateLogger("Startup");
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContextPool<ApiKeyContext>((_, options) => ApiKeyContext.ConfigureOptions(options));
builder.Services.AddHealthChecks()
.AddCheck("WebService", () => HealthCheckResult.Healthy("The web service is running."))
.AddDbContextCheck<ApiKeyContext>();
builder.Services.AddScoped<IApiKeyAccessor, ApiKeyAccessor>()
.AddScoped<IApiKeyRepository, ApiKeyContextRepository>();
builder.Services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme)
.AddApiKeyInHeader(options =>
{
options.Realm = "StalwartSimpleLoginMiddleware";
options.KeyName = "Authentication";
options.Events = new ApiKeyProvider();
});
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
builder.Services.AddControllers()
.AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; });
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
// Add the API Key Security Definition
c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Description =
"API Key needed to access endpoints. Add it to the request headers as 'Authentication: <API_KEY>'",
Type = SecuritySchemeType.ApiKey,
Name = "Authentication", // Header name for the API Key
In = ParameterLocation.Header
});
// Add security requirements to ensure the header is required
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey"
}
},
new string[] { } // No specific scopes
}
});
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>()
.AddSingleton<StalwartClient>();
var app = builder.Build();
// Ensure the database is migrated at startup.
using (var scope = app.Services.CreateScope())
{
try
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApiKeyContext>();
await context.EnsureDatabaseMigrated(logger);
}
catch (NpgsqlException ex)
{
if (ex.SqlState is PostgresErrorCodes.ConnectionFailure or PostgresErrorCodes.ConnectionException ||
ex.Message.StartsWith("Failed to connect"))
logger.LogCritical($"Database connection failed: {ex.Message}");
else if (ex.SqlState is PostgresErrorCodes.InvalidPassword
or PostgresErrorCodes.InvalidAuthorizationSpecification)
logger.LogCritical("Failed to connect. Invalid password.");
else
logger.LogCritical($"An unknown error occurred:{Environment.NewLine}{ex.StackTrace}");
}
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();