Skip to content
quarry Artifact [0ef4cf9bc7]
Login

Artifact [0ef4cf9bc7]

Artifact 0ef4cf9bc79878f1bf3115f998ef07359b5a8f07b393bef4ade0d2faad518f02:


package oltp

import "testing"

// The gate that let a stranger write. A repository created through
// repo.add has no sync users, and "no sync users" used to mean "anyone
// may push" โ€” so every fresh public repository on an internet-facing
// forge accepted a check-in from nobody at all. The letters decide now.
func TestNoSyncUsersFallsBackToTheLetters(t *testing.T) {
	db := testDB(t)
	// Stock: a signed-out visitor holds the nobody category, which
	// carries no i. No sync users, and still gated.
	if !db.Gated("fresh") {
		t.Error("a fresh repository accepted an unauthenticated push")
	}
	if eff := db.EffectiveCaps("", "fresh"); eff.HasAny("i") {
		t.Fatalf("premise broken: a signed-out visitor already holds i (%q)", eff)
	}
}

// The opt-in still works, so a single-user local forge is not broken by
// closing the default. This is the case RFC 0001 ยง7 wanted; it just
// has to be asked for now.
func TestAnonymousPushIsAnOptInThroughTheLetters(t *testing.T) {
	db := testDB(t)
	if err := db.SetCategoryCaps("local", "nobody", "i"); err != nil {
		t.Fatal(err)
	}
	if db.Gated("local") {
		t.Error("a repository whose nobody category grants i still refused an anonymous push")
	}
}

// i reached through a category pull-in counts, because the gate asks
// the EXPANDED set. A raw string match would miss this and refuse a
// push the settings page says is allowed.
func TestInheritedCheckInOpensTheGate(t *testing.T) {
	db := testDB(t)
	// v pulls in the developer category; put i only there.
	if err := db.SetCategoryCaps("inherit", "nobody", "v"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetCategoryCaps("inherit", "developer", "i"); err != nil {
		t.Fatal(err)
	}
	if !db.EffectiveCaps("", "inherit").HasAny("i") {
		t.Skip("this build does not expand v for a signed-out visitor")
	}
	if db.Gated("inherit") {
		t.Error("i inherited through v did not open the gate")
	}
}

// A sync user still decides it, letters or not: naming a pusher is the
// older and more explicit statement of intent.
func TestSyncUsersStillGate(t *testing.T) {
	db := testDB(t)
	if err := db.SetCategoryCaps("named", "nobody", "i"); err != nil {
		t.Fatal(err)
	}
	if db.Gated("named") {
		t.Fatal("premise broken: the letters should have opened this")
	}
	if err := db.SetSyncUser("named", "dan", "secret"); err != nil {
		t.Fatal(err)
	}
	if !db.Gated("named") {
		t.Error("a repository with a sync user accepted an unauthenticated push")
	}
}

// A gate that cannot read its own table must refuse, not wave the
// caller through. This returned false โ€” open โ€” on any query error.
func TestGateFailsClosedWhenItCannotAsk(t *testing.T) {
	db := testDB(t)
	if err := db.SetCategoryCaps("broken", "nobody", "i"); err != nil {
		t.Fatal(err)
	}
	if db.Gated("broken") {
		t.Fatal("premise broken: this should be open before the database goes away")
	}
	// Close the handle underneath it: every query now errors.
	if err := db.db.Close(); err != nil {
		t.Fatal(err)
	}
	if !db.Gated("broken") {
		t.Error("a database that could not answer granted the write")
	}
}