taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

commit e527747c4f9a277fe602c83a5334efcaac243cb7
parent b2f1a3a8f66cd0ee5e6129d624a2c59965caf461
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Fri,  8 Jul 2022 00:45:28 +0200

properly handle and test request back off

Diffstat:
Mcmd/taldir-server/main_test.go | 30++++++++++++++++++++++++++++++
Mpkg/taldir/taldir.go | 48++++++++++++++++++++++++++++++++++++++++++++----
Mtaldir.conf | 2++
3 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go @@ -151,6 +151,36 @@ func TestReRegisterRequest(s *testing.T) { } +func TestReRegisterRequestTooMany(s *testing.T) { + t.ClearDatabase() + + req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest)) + response := executeRequest(req) + + if http.StatusAccepted != response.Code { + s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code) + } + req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest)) + response = executeRequest(req) + + if http.StatusAccepted != response.Code { + s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code) + } + req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest)) + response = executeRequest(req) + + if http.StatusAccepted != response.Code { + s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code) + } + req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest)) + response = executeRequest(req) + + if http.StatusTooManyRequests != response.Code { + s.Errorf("Expected response code %d. Got %d\n", http.StatusTooManyRequests, response.Code) + } + +} + func TestRegisterRequestWrongPubkey(s *testing.T) { t.ClearDatabase() diff --git a/pkg/taldir/taldir.go b/pkg/taldir/taldir.go @@ -74,6 +74,15 @@ type Taldir struct { // Request frequency RequestFrequency int64 + + // Code TTL + CodeTtl time.Duration + + // Code retries max + CodeRetryMax int + + // Code length in bytes before encoding + CodeBytes int } type VersionResponse struct { @@ -182,6 +191,12 @@ type Validation struct { // Public key of the user to register PublicKey string `json:"public_key"` + + // When does this validation timeframe begin (for retry calculation) + TimeframeStart time.Time + + // How often was this validation re-initiated + RetryCount int } type ErrorDetail struct { @@ -376,17 +391,35 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request){ } } err = t.Db.First(&validation, "h_address = ?", validation.HAddress).Error - bytes := t.Cfg.Section("taldir").Key("activation_code_bytes").MustInt(16) - validation.Code = util.GenerateCode(bytes) + validation.Code = util.GenerateCode(t.CodeBytes) validation.Inbox = req.Inbox validation.Duration = req.Duration validation.PublicKey = req.PublicKey if err == nil { // FIXME: Validation already pending for this address // How should we proceed here? Expire old validations? - log.Println("Validation for this address already exists") + if time.Now().Before(validation.TimeframeStart.Add(t.CodeTtl)) { + if validation.RetryCount >= t.CodeRetryMax { + w.WriteHeader(429) + rlResponse := RateLimitedResponse{ + Code: gana.TALDIR_REGISTER_RATE_LIMITED, + RequestFrequency: t.RequestFrequency, + Hint: "Registration rate limit reached", + } + jsonResp, _ := json.Marshal(rlResponse) + w.Write(jsonResp) + t.Db.Delete(&validation) + return + } + validation.RetryCount++ + } else { + log.Println("Validation stale, resetting retry counter") + validation.TimeframeStart = time.Now() + validation.RetryCount = 0 + } err = t.Db.Save(&validation).Error } else { + validation.TimeframeStart = time.Now() err = t.Db.Create(&validation).Error } if err != nil { @@ -482,7 +515,7 @@ func (t *Taldir) DeleteValidation(addr string) error { func (t *Taldir) DeleteEntry(addr string) error { var entry Entry - h := sha512.New() + h := sha512.New() h.Write([]byte(addr)) h_addr := util.EncodeBinaryToString(h.Sum(nil)) hs_address := saltHAddress(h_addr, t.Salt) @@ -621,6 +654,13 @@ func (t *Taldir) Initialize(cfgfile string) { for _, a := range strings.Split(t.Cfg.Section("taldir").Key("validators").String(), " ") { t.Validators[a] = true } + t.CodeBytes = t.Cfg.Section("taldir").Key("activation_code_bytes").MustInt(16) + t.CodeRetryMax = t.Cfg.Section("taldir").Key("activation_retry_max").MustInt(2) + validationTtlStr := t.Cfg.Section("taldir").Key("activation_code_ttl").MustString("5m") + t.CodeTtl, err = time.ParseDuration(validationTtlStr) + if err != nil { + log.Fatal(err) + } psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", t.Cfg.Section("taldir-pq").Key("host").MustString("localhost"), diff --git a/taldir.conf b/taldir.conf @@ -11,6 +11,8 @@ default_doc_lang = en-US default_tos_path = terms/ default_pp_path = privacy/ activation_code_bytes = 16 +activation_retry_max = 2 +activation_code_ttl = 10m [taldir-email] sender = "taldir@taler.net"