feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+50 -2
View File
@@ -52,6 +52,14 @@ func GetHelloWorld(w http.ResponseWriter, _ *http.Request) {
const (
apiKeyMaxRequests = 120
apiKeyWindow = time.Minute
emailChangeMaxRequests = 5
emailChangeWindow = 15 * time.Minute
dataExportWindow = 15 * time.Minute
accountDeleteMaxRequests = 5
accountDeleteWindow = 15 * time.Minute
)
func (c *UseCases) apiKeyAuth() func(http.Handler) http.Handler {
@@ -119,6 +127,7 @@ func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.Auth
verificationHandler := auth.NewVerificationHandler(container.VerifyUserUseCase, container.CancelRegistrationUseCase, isProduction)
meHandler := auth.NewMeHandler(container.GetUserUseCase, container.GetMyAuthorUseCase, container.GetMyAccessUseCase)
passwordResetHandler := auth.NewPasswordResetHandler(container.RequestPasswordResetUseCase, container.ResetPasswordUseCase)
emailChangeHandler := auth.NewEmailChangeHandler(container.RequestEmailChangeUseCase, container.ResendEmailChangeUseCase, container.CancelEmailChangeUseCase, container.ConfirmEmailChangeUseCase)
acceptInviteHandler := auth.NewAcceptInviteHandler(container.GetInviteUseCase, container.AcceptInviteUseCase, container.Token, isProduction)
r.Post("/register", loginHandler.Register)
@@ -149,6 +158,22 @@ func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.Auth
r.Post("/verify", verificationHandler.Verify)
r.Delete("/register", verificationHandler.CancelRegistration)
})
r.Group(func(r chi.Router) {
r.Use(middleware.RequireSession)
r.Use(authtoken.Verifier(container.Token))
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
r.Use(middleware.RequireSignedIn)
r.Use(authtoken.Authenticator)
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
r.Delete("/email/change", emailChangeHandler.Cancel)
r.Post("/email/confirm", emailChangeHandler.Confirm)
r.Group(func(r chi.Router) {
r.Use(middleware.NewUserRateLimiter(emailChangeMaxRequests, emailChangeWindow))
r.Post("/email/change", emailChangeHandler.Request)
r.Post("/email/resend", emailChangeHandler.Resend)
})
})
r.Group(func(r chi.Router) {
r.Use(authtoken.Verifier(container.Token))
r.Use(container.apiKeyAuth())
@@ -159,7 +184,7 @@ func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.Auth
return r
}
func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
func NewUserRouter(container *UseCases, isProduction bool, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
r := chi.NewRouter()
userHandler := user.NewHandler(container.UpdateUserProfileUseCase)
@@ -167,6 +192,8 @@ func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings,
inviteHandler := user.NewInviteHandler(container.CreateInviteUseCase, container.CancelInviteUseCase, container.RenewInviteUseCase, container.ListUsersAndInvitesUseCase)
roleHandler := user.NewRoleHandler(container.ChangeUserRoleUseCase)
removeHandler := user.NewRemoveHandler(container.RemoveUserUseCase)
exportHandler := user.NewExportHandler(container.ExportUserDataUseCase)
deleteAccountHandler := user.NewDeleteAccountHandler(container.DeleteOwnAccountUseCase, isProduction)
r.Group(func(r chi.Router) {
r.Use(authtoken.Verifier(container.Token))
@@ -187,6 +214,27 @@ func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings,
r.Post("/invite/{id}/renew", inviteHandler.RenewInvite)
})
})
r.Group(func(r chi.Router) {
r.Use(middleware.RequireSession)
r.Use(authtoken.Verifier(container.Token))
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
r.Use(middleware.RequireSignedIn)
r.Use(authtoken.Authenticator)
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
r.With(middleware.NewUserSuccessLimiter(dataExportWindow)).Get("/me/export", exportHandler.Export)
})
r.Group(func(r chi.Router) {
r.Use(middleware.RequireSession)
r.Use(authtoken.Verifier(container.Token))
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
r.Use(middleware.RequireSignedIn)
r.Use(authtoken.Authenticator)
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
r.Use(middleware.NewUserRateLimiter(accountDeleteMaxRequests, accountDeleteWindow))
r.Delete("/me", deleteAccountHandler.Delete)
})
r.Group(func(r chi.Router) {
r.Use(middleware.RequireSession)
r.Use(authtoken.Verifier(container.Token))
@@ -535,7 +583,7 @@ func InitBackendRoutes(container *UseCases, settings config.Settings) *chi.Mux {
r.Use(authtoken.Verifier(container.Token))
r.Mount("/article", NewArticleRouter(container, settings.TrustedProxies))
})
r.Mount("/user", NewUserRouter(container, settings.AuthRateLimit, settings.TrustedProxies))
r.Mount("/user", NewUserRouter(container, settings.IsProduction(), settings.AuthRateLimit, settings.TrustedProxies))
r.Mount("/settings", NewSettingsRouter(container, settings.TrustedProxies))
r.Mount("/site", NewSiteRouter(container, settings.TrustedProxies))
r.Mount("/role", NewRoleRouter(container, settings.TrustedProxies))