Rate limits on accounts and inboxes, a database dump before every deploy

- Sign-up, login, the invitation flows and password recovery allow ten
  requests a minute per client address; the inboxes give each sending
  origin (the keyId's) a bucket of 300 that refills at 300 a minute, and
  answer 429 beyond it, which peers retry.
- deploy.yml dumps the PrivaPub database to /var/backups before it stops
  the service (the last seven are kept), and after the swap checks that
  Swagger answers 404 and that the shared inbox answers junk with 400 and
  an unsigned activity with 401.
- ActivityPubClient and PostBoost, never used, are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-01 11:02:21 +02:00
1 parent d2f0c7a14e
commit d80cd42a0c
7 files changed
+72 -34

No files matched your search

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
@@ -11,6 +12,7 @@ using PrivaPub.ClientModels.User;
using PrivaPub.ClientModels.User.Avatar;
using PrivaPub.Extensions;
using PrivaPub.Federation.Actors;
using PrivaPub.Infrastructure;
using PrivaPub.Models;
using PrivaPub.Models.User;
using PrivaPub.Resources;
@@ -56,7 +58,7 @@ namespace PrivaPub.Controllers.ClientToServer
#region User endpoints
[HttpPost, Route("/clientapi/user/signup"), Authorize(Policy = Policies.IsUser), AllowAnonymous]
[HttpPost, Route("/clientapi/user/signup"), EnableRateLimiting(RateLimiting.Accounts), Authorize(Policy = Policies.IsUser), AllowAnonymous]
public async Task<IActionResult> SignUp(LoginForm signUpForm)
{
if (User.Identity?.IsAuthenticated ?? false) return Redirect("/");
@@ -80,7 +82,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/login"), Authorize(Policy = Policies.IsUser), AllowAnonymous]
[HttpPost, Route("/clientapi/user/login"), EnableRateLimiting(RateLimiting.Accounts), Authorize(Policy = Policies.IsUser), AllowAnonymous]
public async Task<IActionResult> Login(LoginForm loginForm)
{
if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions");
@@ -106,7 +108,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/invitation/signup"), AllowAnonymous]
[HttpPost, Route("/clientapi/user/invitation/signup"), EnableRateLimiting(RateLimiting.Accounts), AllowAnonymous]
public async Task<IActionResult> InvitationSignUp(InvitationLoginForm signUpForm, CancellationToken token)
{
var result = new WebResult();
@@ -156,7 +158,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/invitation/login"), AllowAnonymous]
[HttpPost, Route("/clientapi/user/invitation/login"), EnableRateLimiting(RateLimiting.Accounts), AllowAnonymous]
public async Task<IActionResult> InvitationLogin(InvitationLoginForm loginForm, CancellationToken token)
{
var result = new WebResult();
@@ -339,7 +341,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/recover/password"), AllowAnonymous]
[HttpPost, Route("/clientapi/user/recover/password"), EnableRateLimiting(RateLimiting.Accounts), AllowAnonymous]
public async Task<IActionResult> RecoverPassword(PasswordRecoveryForm passwordRecoveryForm)
{
var result = new WebResult();
@@ -363,7 +365,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/recover/valid"), AllowAnonymous]
[HttpPost, Route("/clientapi/user/recover/valid"), EnableRateLimiting(RateLimiting.Accounts), AllowAnonymous]
public async Task<IActionResult> IsValidRecoveryCode(
[FromBody,
Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)),
@@ -386,7 +388,7 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
[HttpPost, Route("/clientapi/user/recover/update/password"), AllowAnonymous]
[HttpPost, Route("/clientapi/user/recover/update/password"), EnableRateLimiting(RateLimiting.Accounts), AllowAnonymous]
public async Task<IActionResult> ChangePassword(NewPasswordForm newPasswordForm)
{
var result = new WebResult();