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 credential row is NOT policy. This is the clause that caused the
// outage: Gated also answered true whenever the repository had any sync
// user, so provisioning one person closed the gate on a repository
// whose letters said anyone could push — and left it closed, because
// the only thing that could issue the row it now demanded was an admin
// running the CLI on the server.
//
// "Must a push authenticate" and "has anybody been issued a credential"
// are different questions. This one answers the first.
func TestACredentialRowIsNotPolicy(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("issuing one person a credential closed the gate on everyone else")
}
}
// 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")
}
}