diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 51ef5a0f..22ca0bb9 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -171,7 +171,7 @@ func registerRoutes(r *gin.Engine, db *gorm.DB, svc *services, rateLimitServices rateLimitMiddleware.Add(middleware.RateLimitDeviceLoginVerification), ) controller.NewOidcController(apiGroup, authMiddleware, fileSizeLimitMiddleware, svc.oidcService, svc.appConfigService) - controller.NewUserController(apiGroup, authMiddleware, fileSizeLimitMiddleware, svc.appConfigService, svc.userService, svc.webauthnModule) + controller.NewUserController(apiGroup, authMiddleware, fileSizeLimitMiddleware, svc.appConfigService, svc.userService, svc.webauthnModule, rateLimitMiddleware.Add(middleware.RateLimitUpdateOwnAccount)) controller.NewAppConfigController(apiGroup, authMiddleware, svc.appConfigService, svc.emailModule) svc.ldapSyncModule.RegisterRoutes(apiGroup, authMiddleware.Add()) controller.NewAppImagesController(apiGroup, authMiddleware, fileSizeLimitMiddleware, svc.appImagesService) diff --git a/backend/internal/controller/upload_size_limit_test.go b/backend/internal/controller/upload_size_limit_test.go index cbcd5796..2378847f 100644 --- a/backend/internal/controller/upload_size_limit_test.go +++ b/backend/internal/controller/upload_size_limit_test.go @@ -55,7 +55,7 @@ func TestImageUploadRoutesLimitRequestSize(t *testing.T) { router := gin.New() router.Use(middleware.NewErrorHandlerMiddleware().Add()) apiGroup := router.Group("/api") - NewUserController(apiGroup, authMiddleware, fileSizeLimitMiddleware, nil, userService, nil) + NewUserController(apiGroup, authMiddleware, fileSizeLimitMiddleware, nil, userService, nil, func(c *gin.Context) { c.Next() }) NewAppImagesController(apiGroup, authMiddleware, fileSizeLimitMiddleware, nil) routes := []string{ diff --git a/backend/internal/controller/user_controller.go b/backend/internal/controller/user_controller.go index 6a9923c8..03b426ce 100644 --- a/backend/internal/controller/user_controller.go +++ b/backend/internal/controller/user_controller.go @@ -20,7 +20,7 @@ import ( // @Summary User management controller // @Description Initializes all user-related API endpoints // @Tags Users -func NewUserController(group *gin.RouterGroup, authMiddleware *middleware.AuthMiddleware, fileSizeLimitMiddleware *middleware.FileSizeLimitMiddleware, appConfigService *appconfig.AppConfigService, userService *service.UserService, webAuthnService *webauthn.Module) { +func NewUserController(group *gin.RouterGroup, authMiddleware *middleware.AuthMiddleware, fileSizeLimitMiddleware *middleware.FileSizeLimitMiddleware, appConfigService *appconfig.AppConfigService, userService *service.UserService, webAuthnService *webauthn.Module, updateOwnAccountRateLimit gin.HandlerFunc) { uc := UserController{ appConfigService: appConfigService, userService: userService, @@ -34,7 +34,8 @@ func NewUserController(group *gin.RouterGroup, authMiddleware *middleware.AuthMi group.PUT("/users/:id", authMiddleware.Add(), httpserver.Handle(uc.updateUserHandler)) group.GET("/users/:id/groups", authMiddleware.Add(), httpserver.Handle(uc.getUserGroupsHandler)) group.GET("/users/:id/webauthn-credentials", authMiddleware.Add(), httpserver.Handle(uc.listUserWebauthnCredentialsHandler)) - group.PUT("/users/me", authMiddleware.WithAdminNotRequired().Add(), httpserver.Handle(uc.updateCurrentUserHandler)) + // Updating the own account reports whether an email or username is already taken, so it is rate limited to slow down probing for existing users + group.PUT("/users/me", authMiddleware.WithAdminNotRequired().Add(), updateOwnAccountRateLimit, httpserver.Handle(uc.updateCurrentUserHandler)) group.DELETE("/users/:id", authMiddleware.Add(), httpserver.Handle(uc.deleteUserHandler)) group.DELETE("/users/:id/webauthn-credentials/:credentialId", authMiddleware.Add(), httpserver.Handle(uc.deleteUserWebauthnCredentialHandler)) diff --git a/backend/internal/middleware/rate_limit.go b/backend/internal/middleware/rate_limit.go index 06885fdd..8acab376 100644 --- a/backend/internal/middleware/rate_limit.go +++ b/backend/internal/middleware/rate_limit.go @@ -19,6 +19,7 @@ import ( const ( RateLimitAPI = "api" RateLimitSignup = "signup" + RateLimitUpdateOwnAccount = "update-own-account" RateLimitWebauthnLogin = "webauthn-login" RateLimitWebauthnReauthenticate = "webauthn-reauthenticate" RateLimitOneTimeAccessToken = "one-time-access-token" @@ -50,6 +51,7 @@ func RateLimitPolicies() []RateLimitPolicy { return []RateLimitPolicy{ {Name: RateLimitAPI, Rate: 100, Per: time.Second, Burst: 300}, {Name: RateLimitSignup, Rate: 2, Per: time.Minute, Burst: 10}, + {Name: RateLimitUpdateOwnAccount, Rate: 2, Per: time.Minute, Burst: 10}, {Name: RateLimitWebauthnLogin, Rate: 1, Per: 5 * time.Second, Burst: 10}, {Name: RateLimitWebauthnReauthenticate, Rate: 1, Per: 10 * time.Second, Burst: 5}, {Name: RateLimitOneTimeAccessToken, Rate: 1, Per: 10 * time.Second, Burst: 5},