Skip to content
quarry Artifact [a2593e2d6f]
Login

Artifact [a2593e2d6f]

Artifact a2593e2d6f051fb14c337d7b9711c78fab4a3635812436f87cfa44505b87f00a:


package xfer

import (
	"net/http"
	"testing"
)

// countingLimit is a LoginLimit that holds a user after max misses.
type countingLimit struct {
	max    int
	misses map[string]int
}

func (l *countingLimit) Allowed(_ *http.Request, user string) bool { return l.misses[user] < l.max }
func (l *countingLimit) Failed(_ *http.Request, user string)       { l.misses[user]++ }
func (l *countingLimit) Succeeded(_ *http.Request, user string)    { delete(l.misses, user) }

// A login card that does not verify against any of the account's
// credentials is a miss against that account (RFC 0021, Rate limits);
// once the limiter holds the account, even a good card proves nothing;
// a verified card clears the count.
func TestLoginCardMissesFeedTheLimiter(t *testing.T) {
	f := New()
	f.SetAuth(secrets{"jan": "right"})
	lim := &countingLimit{max: 2, misses: map[string]int{}}
	f.SetLoginLimit(lim)
	try := func(secret string) loginState {
		body := signedLogin("jan", secret)
		return f.verifiedLogin("alpha", probe(zitUA), body, scanRequest(body))
	}
	if st := try("right"); st.user != "jan" {
		t.Fatalf("FAIL: a good card did not verify")
	}
	try("wrong")
	if lim.misses["jan"] != 1 {
		t.Errorf("FAIL: a bad card counted %d misses, want 1", lim.misses["jan"])
	}
	try("wrong")
	if st := try("right"); st.user != "" {
		t.Errorf("FAIL: a held account still verified")
	} else {
		t.Log("ok: a held account's good card proves nothing")
	}
	delete(lim.misses, "jan")
	try("wrong")
	if st := try("right"); st.user != "jan" || lim.misses["jan"] != 0 {
		t.Errorf("FAIL: a verified card did not clear the count: %+v, misses %d", st, lim.misses["jan"])
	}
	// A card for an account with no credential here is not a token miss.
	body := signedLogin("kim", "anything")
	f.verifiedLogin("alpha", probe(zitUA), body, scanRequest(body))
	if lim.misses["kim"] != 0 {
		t.Errorf("FAIL: an account with no credential was counted")
	}
}