1. CORS whitelist (was '*'): only buhapp.mygoodservice.ru, t.me, web.telegram.org 2. Rate limit on /auth/login (10/min/IP) and /auth/register (5/hour/IP) 3. TGHandler: removed unused JWTSecret, added WebhookSecret, real secret check 4. Login: constant-time bcrypt on user enumeration (dummy hash) 5. TgUsername saved in users (was lost) 6. User.IsVerified=true for Telegram users 7. Register: 409 instead of 500 on duplicate email 8. Bumped version to 0.4.0
86 lines
2.3 KiB
Go
86 lines
2.3 KiB
Go
package handlers
|
||
|
||
import (
|
||
"time"
|
||
|
||
"github.com/gofiber/fiber/v2"
|
||
|
||
"github.com/buhapp/backend/internal/audit"
|
||
"github.com/buhapp/backend/internal/consent"
|
||
)
|
||
|
||
type ConsentHandlers struct {
|
||
Consent *consent.Repo
|
||
Audit *audit.Repo
|
||
}
|
||
|
||
func NewConsentHandlers(c *consent.Repo, a *audit.Repo) *ConsentHandlers {
|
||
return &ConsentHandlers{Consent: c, Audit: a}
|
||
}
|
||
|
||
type consentRequest struct {
|
||
Adult bool `json:"adult"`
|
||
Terms bool `json:"terms"`
|
||
Privacy bool `json:"privacy"`
|
||
Disclaimer bool `json:"disclaimer"`
|
||
}
|
||
|
||
func (h *ConsentHandlers) Accept(c *fiber.Ctx) error {
|
||
me, err := userID(c)
|
||
if err != nil {
|
||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "no user"})
|
||
}
|
||
var req consentRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid JSON"})
|
||
}
|
||
if !req.Adult || !req.Terms || !req.Privacy || !req.Disclaimer {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "all consents required"})
|
||
}
|
||
docs := []struct {
|
||
Type consent.DocType
|
||
}{
|
||
{consent.DocTerms},
|
||
{consent.DocPrivacy},
|
||
{consent.DocDisclaimer},
|
||
{consent.DocAdult},
|
||
}
|
||
for _, d := range docs {
|
||
err := h.Consent.Record(c.UserContext(), &consent.Consent{
|
||
UserID: me,
|
||
DocType: d.Type,
|
||
DocVersion: "1.0",
|
||
IP: c.IP(),
|
||
UserAgent: c.Get("User-Agent"),
|
||
})
|
||
if err != nil {
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "db error"})
|
||
}
|
||
}
|
||
|
||
_ = h.Audit.Log(c.UserContext(), &audit.Event{
|
||
UserID: &me, Action: "consents.accept",
|
||
IP: c.IP(), UserAgent: c.Get("User-Agent"),
|
||
})
|
||
return c.JSON(fiber.Map{"ok": true, "accepted_at": time.Now()})
|
||
}
|
||
|
||
// Status — проверка, какие согласия у пользователя приняты
|
||
func (h *ConsentHandlers) Status(c *fiber.Ctx) error {
|
||
me, err := userID(c)
|
||
if err != nil {
|
||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "no user"})
|
||
}
|
||
accepted := fiber.Map{}
|
||
for _, docType := range []consent.DocType{consent.DocTerms, consent.DocPrivacy, consent.DocDisclaimer, consent.DocAdult} {
|
||
ok, _ := h.Consent.HasAccepted(c.UserContext(), me, docType, "1.0")
|
||
if ok {
|
||
accepted[string(docType)] = true
|
||
}
|
||
}
|
||
allOk := len(accepted) == 4
|
||
return c.JSON(fiber.Map{
|
||
"all_accepted": allOk,
|
||
"accepted": accepted,
|
||
})
|
||
} |