package admin import ( "net/http" "time" "gitea.tbdevent.eu/TBD/reforger_crawler_main/auth" "gitea.tbdevent.eu/TBD/reforger_crawler_main/initializers" "gitea.tbdevent.eu/TBD/reforger_crawler_main/metrics" "github.com/gin-gonic/gin" ) var limiter = auth.NewLoginLimiter(5, 10*time.Minute) func login(c *gin.Context) { if initializers.AdminSecret == "" { fail(c, http.StatusServiceUnavailable, "admin disabled: ADMIN_SECRET not set") return } if c.GetHeader(auth.CSRFHeader) != "1" { fail(c, http.StatusForbidden, "csrf check failed") return } ip := c.ClientIP() if limiter.Blocked(ip) { fail(c, http.StatusTooManyRequests, "too many failed attempts, try again later") return } var body struct { Secret string `json:"secret"` } if err := c.ShouldBindJSON(&body); err != nil { fail(c, http.StatusBadRequest, "invalid JSON") return } if !auth.SecretEqual(body.Secret, initializers.AdminSecret) { limiter.Fail(ip) time.Sleep(300 * time.Millisecond) metrics.AdminActions.Inc("login_failed") metrics.Event("warn", "admin: login failed", map[string]any{"kind": "audit", "ip": ip}) fail(c, http.StatusUnauthorized, "invalid secret") return } limiter.Reset(ip) token, exp := auth.IssueToken(time.Now()) auth.SetSessionCookie(c, token, exp) c.Set("adminAuth", "login") audit(c, "login", nil) c.JSON(http.StatusOK, gin.H{"expiresAt": exp.Unix()}) } func logout(c *gin.Context) { auth.ClearSessionCookie(c) c.Status(http.StatusNoContent) } func me(c *gin.Context) { method := c.GetString("adminAuth") resp := gin.H{"method": method} if v, ok := c.Get("adminSessionExp"); ok { exp := v.(time.Time) // sliding renewal: extend sessions that are about to expire if time.Until(exp) < 24*time.Hour { token, newExp := auth.IssueToken(time.Now()) auth.SetSessionCookie(c, token, newExp) exp = newExp } resp["expiresAt"] = exp.Unix() } c.JSON(http.StatusOK, resp) }