Implement code review fixes and improvements
- Move admin credentials from hardcoded values to configuration - Add rate limiting (5/min) to login endpoint for brute force protection - Extract CleanJsonResponse to shared JsonResponseHelper class - Add DateHelpers.MonthsBetween utility and consolidate date calculations - Update PdfReportService to use ScoreThresholds constants - Remove 5 unused shared components (EmploymentTable, FlagsList, etc.) - Clean up unused CSS from MainLayout.razor.css - Create IPdfReportService interface for better testability - Add authentication requirement to Hangfire dashboard in development - Seal EducationVerifierService class Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System.Threading.RateLimiting;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
using TrueCV.Infrastructure;
|
||||
@@ -35,7 +37,7 @@ try
|
||||
builder.Services.AddInfrastructure(builder.Configuration);
|
||||
|
||||
// Add Web services
|
||||
builder.Services.AddScoped<PdfReportService>();
|
||||
builder.Services.AddScoped<IPdfReportService, PdfReportService>();
|
||||
|
||||
// Add Identity with secure password requirements
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
|
||||
@@ -69,25 +71,52 @@ try
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddDbContextCheck<ApplicationDbContext>("database");
|
||||
|
||||
// Add rate limiting for login endpoint
|
||||
builder.Services.AddRateLimiter(options =>
|
||||
{
|
||||
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
|
||||
options.AddFixedWindowLimiter("login", limiterOptions =>
|
||||
{
|
||||
limiterOptions.PermitLimit = 5;
|
||||
limiterOptions.Window = TimeSpan.FromMinutes(1);
|
||||
limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
||||
limiterOptions.QueueLimit = 0;
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Seed default admin user and clear company cache
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var defaultEmail = "admin@truecv.local";
|
||||
var defaultPassword = "TrueCV_Admin123!";
|
||||
var defaultEmail = builder.Configuration["DefaultAdmin:Email"];
|
||||
var defaultPassword = builder.Configuration["DefaultAdmin:Password"];
|
||||
|
||||
if (await userManager.FindByEmailAsync(defaultEmail) == null)
|
||||
if (!string.IsNullOrEmpty(defaultEmail) && !string.IsNullOrEmpty(defaultPassword))
|
||||
{
|
||||
var adminUser = new ApplicationUser
|
||||
if (await userManager.FindByEmailAsync(defaultEmail) == null)
|
||||
{
|
||||
UserName = defaultEmail,
|
||||
Email = defaultEmail,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
await userManager.CreateAsync(adminUser, defaultPassword);
|
||||
Log.Information("Created default admin user: {Email}", defaultEmail);
|
||||
var adminUser = new ApplicationUser
|
||||
{
|
||||
UserName = defaultEmail,
|
||||
Email = defaultEmail,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
var result = await userManager.CreateAsync(adminUser, defaultPassword);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
Log.Information("Created default admin user: {Email}", defaultEmail);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Failed to create admin user: {Errors}", string.Join(", ", result.Errors.Select(e => e.Description)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("No default admin credentials configured - skipping admin user seeding");
|
||||
}
|
||||
|
||||
// Clear company cache on startup to ensure fresh API lookups
|
||||
@@ -127,13 +156,14 @@ try
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseRateLimiter();
|
||||
|
||||
// Add Hangfire Dashboard (only in development)
|
||||
// Add Hangfire Dashboard (only in development, requires authentication)
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
||||
{
|
||||
Authorization = [] // Allow anonymous access in development
|
||||
Authorization = [new HangfireAuthorizationFilter()]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,7 +203,7 @@ try
|
||||
Log.Warning("Failed login attempt for {Email}", email);
|
||||
return Results.Redirect("/account/login?error=Invalid+email+or+password.");
|
||||
}
|
||||
});
|
||||
}).RequireRateLimiting("login");
|
||||
|
||||
// Logout endpoint
|
||||
app.MapPost("/account/logout", async (SignInManager<ApplicationUser> signInManager) =>
|
||||
|
||||
Reference in New Issue
Block a user