Skip to content
quarry Artifact [c8d5d62ee2]
Login

Artifact [c8d5d62ee2]

Artifact c8d5d62ee25883573e6b32ce522fc44ec1462eb5c4ecdd2d4fda0a6cf7f821f7:


package api

import (
	"errors"
	"testing"

	"craftdesign.group/quarry/internal/ops"
)

// rosterAccounts is a fixed roster; nothing else on AccountStore is
// reachable from the purge rules.
type rosterAccounts struct {
	AccountStore
	list []Account
}

func (r rosterAccounts) Accounts() ([]Account, error) { return r.list, nil }

type memPurgeGrants map[string]bool

func (m memPurgeGrants) GrantPurge(repo, account, by string) error { m[repo+"/"+account] = true; return nil }
func (m memPurgeGrants) RevokePurge(repo, account string) error   { delete(m, repo+"/"+account); return nil }
func (m memPurgeGrants) HasPurgeGrant(repo, account string) bool  { return m[repo+"/"+account] }

// Who may purge: a person who administers the repository; the
// repository's curator (an agent run by a site admin) only once a person
// has granted it; no other agent, a forge agent included.
func TestWhoMayPurge(t *testing.T) {
	grants := memPurgeGrants{}
	d := Deps{Version: "v", Repos: testRepos(wikiIndex(t)), PurgeGrants: grants, Redactions: memRedactions{},
		Accounts: rosterAccounts{list: []Account{
			{Name: "dan"}, {Name: "site", Admin: true}, {Name: "jan"},
			{Name: "curator", Operator: "site"}, {Name: "helper", Operator: "jan"},
			{Name: "zeitgeist", Operator: forgeOperator},
		}}}
	r := ops.NewRegistry()
	if err := RegisterAll(r, d); err != nil {
		t.Fatal(err)
	}
	may := func(who, letters string) bool { return d.mayPurge(as(who, letters), "test") }
	for _, c := range []struct {
		who, letters string
		want         bool
	}{
		{"dan", "a", true},
		{"dan", "k5", false},
		{"zeitgeist", "a", false},
		{"curator", "a", false},
		{"helper", "a", false},
	} {
		if got := may(c.who, c.letters); got != c.want {
			t.Fatalf("FAIL: before any grant, %s holding %q may purge = %v, want %v", c.who, c.letters, got, c.want)
		}
	}
	t.Log("ok: a person admin may; every agent may not")

	if _, err := r.Invoke(as("curator", "a"), "purge.grant", purgeGrantIn{Repo: "test", Account: "curator"}); !errors.Is(err, ops.ErrForbidden) {
		t.Fatalf("FAIL: an agent granting itself purge: err = %v", err)
	}
	for _, who := range []string{"helper", "zeitgeist", "jan"} {
		if _, err := r.Invoke(as("dan", "a"), "purge.grant", purgeGrantIn{Repo: "test", Account: who}); !errors.Is(err, ops.ErrInvalid) {
			t.Fatalf("FAIL: granting purge to non-curator %s: err = %v", who, err)
		}
	}
	if _, err := r.Invoke(as("dan", "a"), "purge.grant", purgeGrantIn{Repo: "test", Account: "curator"}); err != nil {
		t.Fatal(err)
	}
	if !may("curator", "a") || may("curator", "k") {
		t.Fatal("FAIL: a granted curator: may purge with a, and only with a")
	}
	if _, err := r.Invoke(as("dan", "a"), "purge.grant", purgeGrantIn{Repo: "test", Account: "curator", Revoke: true}); err != nil {
		t.Fatal(err)
	}
	if may("curator", "a") {
		t.Fatal("FAIL: a revoked curator may still purge")
	}
	t.Log("ok: only a person grants; only a curator receives; revoke ends it")

	// The fixture's Home has two versions: aaa3 current, aaa2 before it.
	if _, err := r.Invoke(as("dan", "a"), "wiki.purge", purgeIn{Repo: "test", Name: "Home", Rev: "aaa2", Reason: "leaked token"}); !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("FAIL: purging a version that was never redacted: err = %v", err)
	}
	if _, err := r.Invoke(as("jan", "k5"), "wiki.purge", purgeIn{Repo: "test", Name: "Home", Rev: "aaa2", Reason: "x"}); err == nil {
		t.Fatal("FAIL: a non-admin purged")
	}
	t.Log("ok: purge refuses an unredacted version and a non-admin")
}