Skip to content
quarry Artifact [4ba00795a5]
Login

Artifact [4ba00795a5]

Artifact 4ba00795a5012ba15180650fd60d3f6e1abfde7d3dc1b1a56db745e31159e426:


package oltp

import (
	"strings"
	"testing"
)

// RFC 0008 bulletproofing: you cannot lock yourself out, and a
// repository cannot be left without an admin, by a site admin either
// (#318: a site admin holds nothing on a repository ungranted).
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. Nobody may remove it, a site admin included.
	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)
	}
	if warn, err := db.GuardCaps("root", "bob", "repo", Write.Caps()); !IsRefusal(err) {
		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 their own empty string: warn=%q err=%v", warn, err)
	}
	// A site admin granted Admin counts as one of the repository's
	// admins, and cannot take their own away while they are the last.
	if err := db.Grant("root", "repo", RepoAdmin); err != nil {
		t.Fatal(err)
	}
	if err := db.Grant("bob", "repo", Write); err != nil {
		t.Fatal(err)
	}
	if _, err := db.GuardCaps("root", "root", "repo", Write.Caps()); !IsRefusal(err) {
		t.Fatalf("site admin demoting themselves as the last admin: %v", 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)
	}
}