Version 0.2.0
Changes: - forbid to overwrite secrets of other users - add some logs of success - add validation on code (length 50) - use same limiter for POST requests
This commit is contained in:
@@ -8,8 +8,10 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/kanootoko/codevault/internal/db"
|
||||
"github.com/kanootoko/codevault/internal/observability/logging"
|
||||
"github.com/kanootoko/codevault/internal/observability/metrics"
|
||||
"github.com/kanootoko/codevault/internal/ratelimit"
|
||||
)
|
||||
@@ -17,6 +19,7 @@ import (
|
||||
type contextKey string
|
||||
|
||||
const loginContextKey contextKey = "login"
|
||||
const ipContextKey contextKey = "ip"
|
||||
|
||||
// LoginFromContext returns the Basic Auth login stashed by BasicAuthMiddleware,
|
||||
// if any. Strict-server handlers only see context.Context, not *gin.Context, so
|
||||
@@ -26,12 +29,33 @@ func LoginFromContext(ctx context.Context) (string, bool) {
|
||||
return login, ok
|
||||
}
|
||||
|
||||
// IPFromContext returns the client IP stashed by InstrumentMiddleware, if any.
|
||||
// Strict-server handlers only see context.Context, not *gin.Context, so this is
|
||||
// how they recover it.
|
||||
func IPFromContext(ctx context.Context) (string, bool) {
|
||||
ip, ok := ctx.Value(ipContextKey).(string)
|
||||
return ip, ok
|
||||
}
|
||||
|
||||
// BasicAuthMiddleware checks the request's HTTP Basic Auth credentials against
|
||||
// the DB-backed user store.
|
||||
func BasicAuthMiddleware(users *db.UsersRepository, vm metrics.ValueMetrics) gin.HandlerFunc {
|
||||
// the DB-backed user store. It shares the same per-IP failure limiter as
|
||||
// RateLimitMiddleware, so repeated bad credentials get throttled the same way
|
||||
// repeated failed lookups do, instead of the caller being able to hammer the
|
||||
// user store indefinitely.
|
||||
func BasicAuthMiddleware(users *db.UsersRepository, limiter *ratelimit.Limiter, vm metrics.ValueMetrics) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ip := c.ClientIP()
|
||||
if !limiter.Allowed(ip) {
|
||||
limiter.RecordFailure(ip)
|
||||
vm.SetFailedTotal.Add(c.Request.Context(), 1, metric.WithAttributes(attribute.String("reason", "rate_limited")))
|
||||
c.Header("WWW-Authenticate", `Basic realm="codevault"`)
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
login, password, ok := c.Request.BasicAuth()
|
||||
if !ok || users.Verify(c.Request.Context(), login, password) != nil {
|
||||
limiter.RecordFailure(ip)
|
||||
vm.SetFailedTotal.Add(c.Request.Context(), 1, metric.WithAttributes(attribute.String("reason", "unauthorized")))
|
||||
c.Header("WWW-Authenticate", `Basic realm="codevault"`)
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
@@ -64,9 +88,13 @@ func RateLimitMiddleware(limiter *ratelimit.Limiter, vm metrics.ValueMetrics) gi
|
||||
}
|
||||
}
|
||||
|
||||
// InstrumentMiddleware records request count and duration metrics per route.
|
||||
func InstrumentMiddleware(route string, m metrics.HTTPMetrics) gin.HandlerFunc {
|
||||
// InstrumentMiddleware records request count and duration metrics per route. Also injects logger to context.
|
||||
func InstrumentMiddleware(route string, m metrics.HTTPMetrics, logger *zap.Logger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := context.WithValue(c.Request.Context(), ipContextKey, c.ClientIP())
|
||||
ctx = logging.CtxWithLogger(ctx, logger)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
|
||||
start := time.Now()
|
||||
c.Next()
|
||||
duration := time.Since(start).Seconds()
|
||||
|
||||
Reference in New Issue
Block a user