feat: use a FixedWindowRateLimiter keyed by IP to rate limit emails

we don't talk about the sent_emails table :)
This commit is contained in:
sam 2024-12-11 21:17:46 +01:00
parent 1ce4f9d278
commit 51e335f090
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
8 changed files with 75 additions and 87 deletions

View file

@ -36,6 +36,7 @@ public class EmailAuthController(
DatabaseContext db,
AuthService authService,
MailService mailService,
EmailRateLimiter rateLimiter,
KeyCacheService keyCacheService,
UserRendererService userRenderer,
IClock clock,
@ -68,6 +69,9 @@ public class EmailAuthController(
return NoContent();
}
if (IsRateLimited())
return NoContent();
mailService.QueueAccountCreationEmail(req.Email, state);
return NoContent();
}
@ -221,6 +225,9 @@ public class EmailAuthController(
return NoContent();
}
if (IsRateLimited())
return NoContent();
mailService.QueueAddEmailAddressEmail(req.Email, state, CurrentUser.Username);
return NoContent();
}
@ -274,4 +281,34 @@ public class EmailAuthController(
if (!config.EmailAuth.Enabled)
throw new ApiError.BadRequest("Email authentication is not enabled on this instance.");
}
/// <summary>
/// Checks whether the context's IP address is rate limited from dispatching emails.
/// </summary>
private bool IsRateLimited()
{
if (HttpContext.Connection.RemoteIpAddress == null)
{
_logger.Information(
"No remote IP address in HTTP context for email-related request, ignoring as we can't rate limit it"
);
return true;
}
if (
!rateLimiter.IsLimited(
HttpContext.Connection.RemoteIpAddress.ToString(),
out Duration retryAfter
)
)
{
return false;
}
_logger.Information(
"IP address cannot send email until {RetryAfter}, ignoring",
retryAfter
);
return true;
}
}