adding ratelimitter for security measurement

This commit is contained in:
areeqakbr 2025-02-18 23:15:09 +07:00
parent 8d23fdf4aa
commit 527794ea76
7 changed files with 99 additions and 3 deletions

View File

@ -19,6 +19,7 @@ type BackboneController struct {
func (bc *BackboneController) Route() { func (bc *BackboneController) Route() {
rg := bc.rg.Group("/backbone") rg := bc.rg.Group("/backbone")
rg.Use(middleware.AuthMiddleware()) rg.Use(middleware.AuthMiddleware())
rg.Use(middleware.RateLimitMiddleware())
{ {
rg.GET("", bc.GetBackbone()) rg.GET("", bc.GetBackbone())
rg.POST("", bc.CreateBackbone()) rg.POST("", bc.CreateBackbone())

View File

@ -2,6 +2,7 @@ package controller
import ( import (
"net/http" "net/http"
"users_management/m/middleware"
"users_management/m/model/dto" "users_management/m/model/dto"
"users_management/m/usecase" "users_management/m/usecase"
"users_management/m/utils/common" "users_management/m/utils/common"
@ -17,7 +18,10 @@ type UsersController struct {
func (uc *UsersController) Route() { func (uc *UsersController) Route() {
rg:= uc.rg.Group("/users") rg:= uc.rg.Group("/users")
rg.Use(middleware.RateLoginMiddleware())
{
rg.POST("/login", uc.Login()) rg.POST("/login", uc.Login())
}
rg.POST("/logout", uc.Logout()) rg.POST("/logout", uc.Logout())
} }

1
go.mod
View File

@ -44,6 +44,7 @@ require (
golang.org/x/sync v0.11.0 // indirect golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.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 google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

2
go.sum
View File

@ -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/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 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=

View File

@ -18,7 +18,7 @@ type infraManager struct {
} }
func (im *infraManager) openConn() error { 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) im.cfg.DBHost, im.cfg.DBUser, im.cfg.DBPass, im.cfg.DBName, im.cfg.DBPort)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil { if err != nil {

View File

@ -1,9 +1,11 @@
package middleware package middleware
import ( import (
"encoding/json"
"net/http" "net/http"
"strings" "strings"
"users_management/m/model/dto/res"
"users_management/m/utils/common" "users_management/m/utils/common"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -50,6 +52,15 @@ func AuthMiddleware() gin.HandlerFunc {
return 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() c.Next()
} }

View File

@ -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()
}
}