Skip to content
quarry Artifact [17670bc0d9]
Login

Artifact [17670bc0d9]

Artifact 17670bc0d9bd56ed23299ace23ac81112bc1439ed4c47bbc9a57655ee2bfc7d7:


package oltp

import (
	"strings"
	"testing"
)

// RFC 0008 bulletproofing: you cannot lock yourself out, and a
// repository cannot be left without an admin by anyone but a site
// admin, who is told.
func TestGuardRefusesSelfLockoutAndLastAdmin(t *testing.T) {
	db := testDB(t)
	for _, n := range []string{"alice", "bob"} {
		if err := db.AddAccount(n, "pw12345678", false); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.AddAccount("root", "pw12345678", true); err != nil {
		t.Fatal(err)
	}
	if err := db.Grant("alice", "repo", RepoAdmin); err != nil {
		t.Fatal(err)
	}
	if err := db.Grant("bob", "repo", Write); err != nil {
		t.Fatal(err)
	}
	if _, err := db.GuardCaps("alice", "alice", "repo", Write.Caps()); !IsRefusal(err) || !strings.Contains(err.Error(), "your own admin") {
		t.Fatalf("self-demotion: %v", err)
	}
	if _, err := db.GuardCaps("alice", "bob", "repo", Maintain.Caps()); err != nil {
		t.Fatalf("promoting bob: %v", err)
	}
	if err := db.Grant("bob", "repo", RepoAdmin); err != nil {
		t.Fatal(err)
	}
	if _, err := db.GuardCaps("alice", "alice", "repo", Write.Caps()); err == nil {
		t.Fatal("still your own admin, even with bob there")
	}
	if _, err := db.GuardCaps("bob", "alice", "repo", Write.Caps()); err != nil {
		t.Fatalf("bob demoting alice with bob remaining: %v", err)
	}
	if err := db.Grant("alice", "repo", Write); err != nil {
		t.Fatal(err)
	}
	// Only bob is admin now. alice may not remove it; root may, and hears.
	if _, err := db.GuardCaps("alice", "bob", "repo", Write.Caps()); !IsRefusal(err) || !strings.Contains(err.Error(), "no admin") {
		t.Fatalf("last admin by a non-site-admin: %v", err)
	}
	warn, err := db.GuardCaps("root", "bob", "repo", Write.Caps())
	if err != nil || !strings.Contains(warn, "no admin of its own") {
		t.Fatalf("site admin removing the last admin: warn=%q err=%v", warn, err)
	}
	if warn, err := db.GuardCaps("root", "root", "repo", ""); err != nil || warn != "" {
		t.Fatalf("site admin editing self: warn=%q err=%v", warn, err)
	}
}

func TestMinimalAndChange(t *testing.T) {
	if got := Minimal("uv23bBC7"); got != "uv3bBC7" {
		t.Fatalf("Minimal dropped the wrong letters: %q", got)
	}
	if got := Minimal("wrcn"); got != "w" {
		t.Fatalf("w implies rcn: %q", got)
	}
	if got := Change("uv3bBC7", "uv3bBC7"); got != "" {
		t.Fatalf("no change said %q", got)
	}
	got := Change("", Write.Caps())
	if !strings.HasPrefix(got, "added as Developer") {
		t.Fatalf("adding: %q", got)
	}
	got = Change(Write.Caps(), Maintain.Caps())
	if !strings.HasPrefix(got, "Developer → Maintainer; gains: ") {
		t.Fatalf("promotion: %q", got)
	}
	if got := Change(Write.Caps(), ""); got != "removed from the repository" {
		t.Fatalf("removal: %q", got)
	}
}