From 527794ea76a5d6308c63182478eff25e302c947a Mon Sep 17 00:00:00 2001 From: areeqakbr Date: Tue, 18 Feb 2025 23:15:09 +0700 Subject: [PATCH 1/3] adding ratelimitter for security measurement --- delivery/controller/backbone_controller.go | 1 + delivery/controller/users_controller.go | 6 +- go.mod | 1 + go.sum | 2 + manager/infra_manager.go | 2 +- middleware/auth_middleware.go | 13 +++- middleware/rate_limitter.go | 77 ++++++++++++++++++++++ 7 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 middleware/rate_limitter.go diff --git a/delivery/controller/backbone_controller.go b/delivery/controller/backbone_controller.go index 162242a..8442a02 100644 --- a/delivery/controller/backbone_controller.go +++ b/delivery/controller/backbone_controller.go @@ -19,6 +19,7 @@ type BackboneController struct { func (bc *BackboneController) Route() { rg := bc.rg.Group("/backbone") rg.Use(middleware.AuthMiddleware()) + rg.Use(middleware.RateLimitMiddleware()) { rg.GET("", bc.GetBackbone()) rg.POST("", bc.CreateBackbone()) diff --git a/delivery/controller/users_controller.go b/delivery/controller/users_controller.go index dfed6a6..94d413c 100644 --- a/delivery/controller/users_controller.go +++ b/delivery/controller/users_controller.go @@ -2,6 +2,7 @@ package controller import ( "net/http" + "users_management/m/middleware" "users_management/m/model/dto" "users_management/m/usecase" "users_management/m/utils/common" @@ -17,7 +18,10 @@ type UsersController struct { func (uc *UsersController) Route() { rg:= uc.rg.Group("/users") - rg.POST("/login", uc.Login()) + rg.Use(middleware.RateLoginMiddleware()) + { + rg.POST("/login", uc.Login()) + } rg.POST("/logout", uc.Logout()) } diff --git a/go.mod b/go.mod index fcd0657..c9385cf 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect + golang.org/x/time v0.10.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 31572a3..b5f466a 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,8 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= diff --git a/manager/infra_manager.go b/manager/infra_manager.go index f49da48..6ac151a 100644 --- a/manager/infra_manager.go +++ b/manager/infra_manager.go @@ -18,7 +18,7 @@ type infraManager struct { } func (im *infraManager) openConn() error { - dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=require TimeZone=Asia/Shanghai", + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s TimeZone=Asia/Shanghai", im.cfg.DBHost, im.cfg.DBUser, im.cfg.DBPass, im.cfg.DBName, im.cfg.DBPort) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { diff --git a/middleware/auth_middleware.go b/middleware/auth_middleware.go index d2878c9..beaa0dd 100644 --- a/middleware/auth_middleware.go +++ b/middleware/auth_middleware.go @@ -1,9 +1,11 @@ package middleware import ( + "encoding/json" "net/http" "strings" - + + "users_management/m/model/dto/res" "users_management/m/utils/common" "github.com/gin-gonic/gin" @@ -50,6 +52,15 @@ func AuthMiddleware() gin.HandlerFunc { return } + var authResponse res.AuthMeResponse + if err := json.NewDecoder(resp.Body).Decode(&authResponse); err != nil { + common.ErrorResponses(c, http.StatusInternalServerError, err.Error()) + c.Abort() + return + } + + c.Set("userID", authResponse.Data.NomorInduk) + c.Next() } diff --git a/middleware/rate_limitter.go b/middleware/rate_limitter.go new file mode 100644 index 0000000..39b0256 --- /dev/null +++ b/middleware/rate_limitter.go @@ -0,0 +1,77 @@ +package middleware + +import ( + "net/http" + "sync" + "time" + "users_management/m/utils/common" + + "github.com/gin-gonic/gin" + "golang.org/x/time/rate" +) + +var ( + rateLimiters = make(map[string]*rate.Limiter) + mu sync.Mutex +) + +func getRateLimiter(userID string) *rate.Limiter { + mu.Lock() + defer mu.Unlock() + + limiter, exists := rateLimiters[userID] + if !exists { + limiter = rate.NewLimiter(rate.Every(1*time.Minute), 50) // 1 request per second with a burst of 2 requests + rateLimiters[userID] = limiter + } + + return limiter +} + +func getLoginLimiter() *rate.Limiter { + mu.Lock() + defer mu.Unlock() + + limiter, exists := rateLimiters["login"] + if !exists { + limiter = rate.NewLimiter(rate.Every(1*time.Minute), 4) // 5 request per second with a burst of 10 requests + rateLimiters["login"] = limiter + } + + return limiter +} + +func RateLimitMiddleware() gin.HandlerFunc{ + return func(c *gin.Context) { + userID, exists := c.Get("userID") + if !exists { + common.ErrorResponses(c, http.StatusUnauthorized, "Unauthorized: No user ID found") + c.Abort() + return + } + + limiter := getRateLimiter(userID.(string)) + + if !limiter.Allow() { + common.ErrorResponses(c, http.StatusTooManyRequests, "Too many requests") + c.Abort() + return + } + + c.Next() + } +} + +func RateLoginMiddleware() gin.HandlerFunc{ + return func(c *gin.Context) { + limiter := getLoginLimiter() + + if !limiter.Allow() { + common.ErrorResponses(c, http.StatusTooManyRequests, "Too many requests") + c.Abort() + return + } + c.Next() + } +} + From 6af9a26717826b7947359597b2431906ffaffdbd Mon Sep 17 00:00:00 2001 From: areeqakbr Date: Tue, 18 Feb 2025 23:16:16 +0700 Subject: [PATCH 2/3] adding require for main only --- manager/infra_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/infra_manager.go b/manager/infra_manager.go index 6ac151a..f49da48 100644 --- a/manager/infra_manager.go +++ b/manager/infra_manager.go @@ -18,7 +18,7 @@ type infraManager struct { } func (im *infraManager) openConn() error { - dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s TimeZone=Asia/Shanghai", + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=require TimeZone=Asia/Shanghai", im.cfg.DBHost, im.cfg.DBUser, im.cfg.DBPass, im.cfg.DBName, im.cfg.DBPort) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { From 36063063104fe131af1c40751a8cb3b1a33d8b31 Mon Sep 17 00:00:00 2001 From: areeqakbr Date: Tue, 18 Feb 2025 23:30:02 +0700 Subject: [PATCH 3/3] adding to another routes --- delivery/controller/devicePort_controller.go | 1 + delivery/controller/devices_controller.go | 1 + delivery/controller/fishbone_controller.go | 1 + delivery/controller/tower_controller.go | 1 + 4 files changed, 4 insertions(+) diff --git a/delivery/controller/devicePort_controller.go b/delivery/controller/devicePort_controller.go index 0216e00..aa4f8e1 100644 --- a/delivery/controller/devicePort_controller.go +++ b/delivery/controller/devicePort_controller.go @@ -19,6 +19,7 @@ type DevicePortController struct { func (dc *DevicePortController) Route() { rg := dc.rg.Group("/device-port") rg.Use(middleware.AuthMiddleware()) + rg.Use(middleware.RateLimitMiddleware()) { rg.GET("", dc.GetDevicePort()) rg.POST("", dc.CreateDevicePort()) diff --git a/delivery/controller/devices_controller.go b/delivery/controller/devices_controller.go index 680a96a..e82cfea 100644 --- a/delivery/controller/devices_controller.go +++ b/delivery/controller/devices_controller.go @@ -19,6 +19,7 @@ type DeviceController struct { func (dc *DeviceController) Route() { rg := dc.rg.Group("/devices") rg.Use(middleware.AuthMiddleware()) + rg.Use(middleware.RateLimitMiddleware()) { rg.POST("", dc.CreateDevice()) rg.GET("", dc.GetAllDevices()) diff --git a/delivery/controller/fishbone_controller.go b/delivery/controller/fishbone_controller.go index ca9f8f4..863be49 100644 --- a/delivery/controller/fishbone_controller.go +++ b/delivery/controller/fishbone_controller.go @@ -19,6 +19,7 @@ type FishboneController struct { func (fc *FishboneController) Route() { rg := fc.rg.Group("/fishbone") rg.Use(middleware.AuthMiddleware()) + rg.Use(middleware.RateLimitMiddleware()) { rg.GET("", fc.GetFishbone()) rg.POST("", fc.CreateFishbone()) diff --git a/delivery/controller/tower_controller.go b/delivery/controller/tower_controller.go index 93869f0..f86dffe 100644 --- a/delivery/controller/tower_controller.go +++ b/delivery/controller/tower_controller.go @@ -19,6 +19,7 @@ type TowerController struct { func (tc *TowerController) Route() { rg := tc.rg.Group("/tower") rg.Use(middleware.AuthMiddleware()) + rg.Use(middleware.RateLimitMiddleware()) { rg.GET("", tc.GetTower()) rg.POST("", tc.CreateTower())