Add six-month domain and broker monitoring
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.RateLimiting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using SGU.AuthBroker;
|
||||
using SGU.AuthBroker.Contracts;
|
||||
using SGU.AuthBroker.Core.Authentication;
|
||||
using SGU.AuthBroker.Core.Directory;
|
||||
@@ -10,6 +12,16 @@ using SGU.AuthBroker.Services;
|
||||
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
builder.Host.UseWindowsService(options => options.ServiceName = "SGU Authentication Broker");
|
||||
if (builder.Configuration.GetValue("Broker:Diagnostics:UseDedicatedEventLog", false))
|
||||
{
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddEventLog(settings =>
|
||||
{
|
||||
settings.LogName = "SGU Auth Broker";
|
||||
settings.SourceName = "SGU.AuthBroker.Operational";
|
||||
settings.Filter = (_, level) => level >= LogLevel.Information;
|
||||
});
|
||||
}
|
||||
|
||||
BrokerOptions brokerOptions = builder.Configuration
|
||||
.GetSection(BrokerOptions.SectionName)
|
||||
@@ -56,6 +68,12 @@ builder.Services.AddRateLimiter(options =>
|
||||
});
|
||||
|
||||
WebApplication app = builder.Build();
|
||||
ILogger auditLogger = app.Services.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger("SGU.AuthBroker.Audit");
|
||||
auditLogger.LogInformation(
|
||||
BrokerEventIds.BrokerStarted,
|
||||
"SGU Authentication Broker started with dedicated operational diagnostics enabled={DedicatedDiagnosticsEnabled}.",
|
||||
builder.Configuration.GetValue("Broker:Diagnostics:UseDedicatedEventLog", false));
|
||||
app.UseRateLimiter();
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
@@ -75,16 +93,58 @@ app.MapPost("/v1/authenticate", async (
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Password) || request.Password.Length > 256)
|
||||
{
|
||||
auditLogger.LogInformation(
|
||||
BrokerEventIds.AuthenticationInvalidRequest,
|
||||
"Authentication request rejected before validation for {InstitutionalUser}: password was missing or outside the supported length.",
|
||||
SafeUserName(request.Clave));
|
||||
request.ReleasePasswordReference();
|
||||
return Results.BadRequest(new ErrorResponse("MISSING_PASSWORD", "La contraseña es requerida."));
|
||||
}
|
||||
|
||||
Stopwatch elapsed = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
AuthenticationFlowResult result = await workflow
|
||||
.AuthenticateAsync(request.Clave, request.Password, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
string institutionalUser = result.Identity?.UserName ?? SafeUserName(request.Clave);
|
||||
switch (result.Outcome)
|
||||
{
|
||||
case AuthenticationFlowOutcome.Authorized:
|
||||
auditLogger.LogInformation(
|
||||
BrokerEventIds.AuthenticationAuthorized,
|
||||
"Authentication completed for {InstitutionalUser} with role {Role} in {ElapsedMilliseconds} ms. AD created={Created}; moved={Moved}.",
|
||||
institutionalUser,
|
||||
result.Identity!.Role,
|
||||
elapsed.ElapsedMilliseconds,
|
||||
result.Directory!.Created,
|
||||
result.Directory.Moved);
|
||||
break;
|
||||
case AuthenticationFlowOutcome.InvalidCredentials:
|
||||
auditLogger.LogInformation(
|
||||
BrokerEventIds.AuthenticationRejected,
|
||||
"Authentication was rejected for {InstitutionalUser} with code {ErrorCode} after {ElapsedMilliseconds} ms.",
|
||||
institutionalUser,
|
||||
result.ErrorCode,
|
||||
elapsed.ElapsedMilliseconds);
|
||||
break;
|
||||
case AuthenticationFlowOutcome.Unavailable:
|
||||
auditLogger.LogWarning(
|
||||
BrokerEventIds.AuthenticationUnavailable,
|
||||
"Authentication was unavailable for {InstitutionalUser} with code {ErrorCode} after {ElapsedMilliseconds} ms.",
|
||||
institutionalUser,
|
||||
result.ErrorCode,
|
||||
elapsed.ElapsedMilliseconds);
|
||||
break;
|
||||
default:
|
||||
auditLogger.LogInformation(
|
||||
BrokerEventIds.AuthenticationInvalidRequest,
|
||||
"Authentication request had an invalid institutional user format after {ElapsedMilliseconds} ms.",
|
||||
elapsed.ElapsedMilliseconds);
|
||||
break;
|
||||
}
|
||||
|
||||
return result.Outcome switch
|
||||
{
|
||||
AuthenticationFlowOutcome.Authorized => Results.Ok(new AuthenticationResponse(
|
||||
@@ -125,3 +185,11 @@ static IResult Unavailable(HttpContext context, string? errorCode)
|
||||
|
||||
static string NormalizeThumbprint(string value) =>
|
||||
value.Replace(" ", string.Empty, StringComparison.Ordinal).ToUpperInvariant();
|
||||
|
||||
static string SafeUserName(string? value)
|
||||
{
|
||||
string candidate = value?.Trim().ToUpperInvariant() ?? string.Empty;
|
||||
return candidate.Length is > 0 and <= 16 && candidate.All(char.IsAsciiLetterOrDigit)
|
||||
? candidate
|
||||
: "<invalid-format>";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user