metarefresh.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. package metarefresh
  2. import (
  3. "crypto/subtle"
  4. "fmt"
  5. "log/slog"
  6. "net/http"
  7. "time"
  8. "github.com/TecharoHQ/anubis"
  9. "github.com/TecharoHQ/anubis/lib/challenge"
  10. "github.com/TecharoHQ/anubis/lib/localization"
  11. "github.com/a-h/templ"
  12. )
  13. //go:generate go tool github.com/a-h/templ/cmd/templ generate
  14. func init() {
  15. challenge.Register("metarefresh", &Impl{})
  16. }
  17. type Impl struct{}
  18. func (i *Impl) Setup(mux *http.ServeMux) {}
  19. func (i *Impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *challenge.IssueInput) (templ.Component, error) {
  20. u, err := r.URL.Parse(anubis.BasePrefix + "/.within.website/x/cmd/anubis/api/pass-challenge")
  21. if err != nil {
  22. return nil, fmt.Errorf("can't render page: %w", err)
  23. }
  24. q := u.Query()
  25. q.Set("redir", r.URL.String())
  26. q.Set("challenge", in.Challenge.RandomData)
  27. q.Set("id", in.Challenge.ID)
  28. u.RawQuery = q.Encode()
  29. showMeta := in.Challenge.RandomData[0]%2 == 0
  30. if !showMeta {
  31. w.Header().Add("Refresh", fmt.Sprintf("%d; url=%s", in.Rule.Challenge.Difficulty+1, u.String()))
  32. }
  33. loc := localization.GetLocalizer(r)
  34. result := page(u.String(), in.Rule.Challenge.Difficulty, showMeta, loc)
  35. return result, nil
  36. }
  37. func (i *Impl) Validate(r *http.Request, lg *slog.Logger, in *challenge.ValidateInput) error {
  38. wantTime := in.Challenge.IssuedAt.Add(time.Duration(in.Rule.Challenge.Difficulty) * 800 * time.Millisecond)
  39. if time.Now().Before(wantTime) {
  40. return challenge.NewError("validate", "insufficient time", fmt.Errorf("%w: wanted user to wait until at least %s", challenge.ErrFailed, wantTime.Format(time.RFC3339)))
  41. }
  42. gotChallenge := r.FormValue("challenge")
  43. if subtle.ConstantTimeCompare([]byte(in.Challenge.RandomData), []byte(gotChallenge)) != 1 {
  44. return challenge.NewError("validate", "invalid response", fmt.Errorf("%w: wanted response %s but got %s", challenge.ErrFailed, in.Challenge.RandomData, gotChallenge))
  45. }
  46. return nil
  47. }