Skip to content
quarry Artifact [e32cb1c377]
Login

Artifact [e32cb1c377]

Artifact e32cb1c377dea5788fb3a3bf8092fefce260fdf021197ec7c2e64d7a8d7b2b70:


package api

import (
	"errors"
	"testing"

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

// RFC 0021, Testing: Forge agents (the doors). agent.new with
// operator forge is a site admin's and needs answerable; any site admin
// mints, revokes and reassigns; a forge agent holding s passes
// account.add and the deed paths and is refused on making or altering
// a forge agent and on any s flag change.
func TestForgeAgentIsASiteAdminsToMakeAndManage(t *testing.T) {
	r, db, _ := agentCoreRig(t, 5)
	if _, err := r.Invoke(asUser("jva"), "agent.new", agentNewIn{Name: "fbot", Operator: "forge", Answerable: "dmestas"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a person created a forge agent: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "agent.new", agentNewIn{Name: "fbot", Operator: "forge"}); !errors.Is(err, ops.ErrInvalid) {
		t.Errorf("FAIL: a forge agent without answerable: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "agent.new", agentNewIn{Name: "fbot", Operator: "forge", Answerable: "jva"}); !errors.Is(err, ops.ErrInvalid) {
		t.Errorf("FAIL: a person without the flag became answerable: %v", err)
	}
	out, err := r.Invoke(asUser("dmestas"), "agent.new", agentNewIn{Name: "fbot", Operator: "forge", Answerable: "dmestas"})
	if err != nil {
		t.Fatalf("FAIL: %v", err)
	}
	if out.(agentOut).Operator != "forge" || db.Operator("fbot") != "forge" {
		t.Errorf("FAIL: created %+v, operator %q", out, db.Operator("fbot"))
	}
	// Tokens: any site admin mints and revokes; a person does not.
	minted, err := r.Invoke(asUser("dmestas"), "agent.token.new", agentTokenNewIn{Agent: "fbot", Name: "s", Scopes: "api", Days: 30})
	if err != nil {
		t.Fatalf("FAIL: the site admin could not mint for a forge agent: %v", err)
	}
	if _, err := r.Invoke(asUser("jva"), "agent.token.new", agentTokenNewIn{Agent: "fbot", Name: "x", Scopes: "api"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a person minted for a forge agent: %v", err)
	}
	if _, err := r.Invoke(asUser("jva"), "agent.token.revoke", agentTokenRevokeIn{Agent: "fbot", ID: minted.(agentTokenNewOut).Token.ID}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a person revoked a forge agent's token: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "agent.token.revoke", agentTokenRevokeIn{Agent: "fbot", ID: minted.(agentTokenNewOut).Token.ID}); err != nil {
		t.Errorf("FAIL: the site admin could not revoke: %v", err)
	}
	// The answerable admin: any site admin reassigns.
	if err := db.AddAccount("root2", "a-long-enough-password", true); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("jva"), "account.answerable", accountAnswerableIn{Name: "fbot", Answerable: "root2"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a person reassigned the answerable admin: %v", err)
	}
	if _, err := r.Invoke(asUser("root2"), "account.answerable", accountAnswerableIn{Name: "fbot", Answerable: "root2"}); err != nil {
		t.Errorf("FAIL: a site admin could not reassign: %v", err)
	}
	out, err = r.Invoke(asUser("dmestas"), "agent.list", agentListIn{Operator: "forge"})
	if err != nil {
		t.Fatal(err)
	}
	if rows := out.(agentListOut).Agents; len(rows) != 1 || rows[0].Answerable != "root2" || rows[0].Operator != "forge" {
		t.Errorf("FAIL: the fleet list: %+v", rows)
	}
	// Disabling the answerable admin: the agent works, nobody answers.
	if _, err := r.Invoke(asUser("dmestas"), "account.disable", accountDisableIn{Name: "root2", On: true}); err != nil {
		t.Fatal(err)
	}
	out, _ = r.Invoke(asUser("dmestas"), "agent.list", agentListIn{Operator: "forge"})
	if rows := out.(agentListOut).Agents; rows[0].Answerable != "" || rows[0].Disabled != "" {
		t.Errorf("FAIL: after the admin's disabling: %+v", rows)
	} else {
		t.Log("ok: a forge agent is a site admin's to make, mint for, revoke and reassign; its admin's loss leaves it working")
	}
}

func TestForgeAgentHoldingSStopsAtOtherForgeAgentsAndTheFlag(t *testing.T) {
	r, db, owners := agentCoreRig(t, 5)
	// The migration shape: a site admin re-marked as a forge agent,
	// keeping the flag.
	if err := db.AddAccount("zeitgeist", "a-long-enough-password", true); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("jva"), "account.agent", accountAgentIn{Name: "zeitgeist", Operator: "forge", Answerable: "dmestas"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a person re-marked a forge agent: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "account.agent", accountAgentIn{Name: "zeitgeist", Operator: "forge"}); !errors.Is(err, ops.ErrInvalid) {
		t.Errorf("FAIL: re-marking without answerable: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "account.agent", accountAgentIn{Name: "zeitgeist", Operator: "forge", Answerable: "dmestas"}); err != nil {
		t.Fatalf("FAIL: %v", err)
	}
	z, _ := db.AccountByName("zeitgeist")
	if !z.Admin || z.Operator != "forge" {
		t.Fatalf("FAIL: re-marked reads %+v", z)
	}
	if _, ok := db.Authenticate("zeitgeist", "a-long-enough-password"); ok {
		t.Error("FAIL: the re-marked forge agent still signs in with its password")
	}
	// What a site admin may, it may: a person's account, a person's
	// agent, a deed.
	if _, err := r.Invoke(asUser("zeitgeist"), "account.add", accountAddIn{Name: "newperson", Password: "a-long-enough-password"}); err != nil {
		t.Errorf("FAIL: a forge agent with s could not add a person: %v", err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.agent", accountAgentIn{Name: "newperson", Operator: "jva"}); err != nil {
		t.Errorf("FAIL: a forge agent with s could not mark a person's agent: %v", err)
	}
	if err := db.Grant("zeitgeist", "test", oltp.RepoAdmin); err != nil {
		t.Fatal(err)
	}
	owners.owner["test"] = ""
	if _, err := r.Invoke(asUser("zeitgeist"), "repo.claim", repoClaimIn{Repo: "test"}); err != nil {
		t.Errorf("FAIL: a forge agent could not claim an unheld repository: %v", err)
	}
	if owners.owner["test"] != "zeitgeist" {
		t.Errorf("FAIL: the deed went to %q", owners.owner["test"])
	}
	d := Deps{Owners: owners, Accounts: acctAdapter{db}}
	if err := d.refuseUnlessTheirs(asUser("zeitgeist"), "test", "destroy"); err != nil {
		t.Errorf("FAIL: a forge agent holding the deed was refused a deed act: %v", err)
	}
	// A deed held by a site admin or another forge agent is past a
	// forge agent's reach: it neither hands it on nor destroys under
	// it; a person with s still does both, and a plain person's deed
	// is the operator path as today.
	if err := db.AddAccount("admin2", "a-long-enough-password", true); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "agent.new", agentNewIn{Name: "fbot0", Operator: "forge", Answerable: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	for _, who := range []string{"admin2", "fbot0", "jva"} {
		if err := db.Grant(who, "test", oltp.RepoAdmin); err != nil {
			t.Fatal(err)
		}
	}
	for _, holder := range []string{"admin2", "fbot0"} {
		owners.owner["test"] = holder
		if _, err := r.Invoke(asUser("zeitgeist"), "repo.claim", repoClaimIn{Repo: "test", Account: "jva"}); !errors.Is(err, ops.ErrForbidden) {
			t.Errorf("FAIL: a forge agent moved %s's deed: %v", holder, err)
		}
		if err := d.refuseUnlessTheirs(asUser("zeitgeist"), "test", "destroy"); !errors.Is(err, ops.ErrForbidden) {
			t.Errorf("FAIL: a forge agent may destroy under %s's deed: %v", holder, err)
		}
		if owners.owner["test"] != holder {
			t.Errorf("FAIL: the deed left %s", holder)
		}
	}
	if err := d.refuseUnlessTheirs(asUser("dmestas"), "test", "destroy"); err != nil {
		t.Errorf("FAIL: a person with s was refused a deed act: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "repo.claim", repoClaimIn{Repo: "test", Account: "jva"}); err != nil {
		t.Errorf("FAIL: a person with s could not hand a forge agent's deed on: %v", err)
	}
	owners.owner["test"] = "jva"
	if err := d.refuseUnlessTheirs(asUser("zeitgeist"), "test", "destroy"); err != nil {
		t.Errorf("FAIL: a forge agent was refused under a plain person's deed: %v", err)
	}
	owners.owner["test"] = "zeitgeist"
	// Its own tokens, yes; another forge agent, never.
	if _, err := r.Invoke(asUser("zeitgeist"), "agent.token.new", agentTokenNewIn{Agent: "zeitgeist", Name: "own", Scopes: "api"}); err != nil {
		t.Errorf("FAIL: a forge agent could not mint its own token: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "agent.new", agentNewIn{Name: "fbot", Operator: "forge", Answerable: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	refused := map[string]any{
		"agent.new":          agentNewIn{Name: "fbot2", Operator: "forge", Answerable: "dmestas"},
		"account.agent":      accountAgentIn{Name: "fbot", Operator: "jva"},
		"account.answerable": accountAnswerableIn{Name: "fbot", Answerable: "dmestas"},
		"agent.token.new":    agentTokenNewIn{Agent: "fbot", Name: "x", Scopes: "api"},
		"agent.delete":       agentNameIn{Name: "fbot"},
		"account.add":        accountAddIn{Name: "newadmin", Password: "a-long-enough-password", Admin: true},
		"account.invite":     accountInviteIn{Name: "invitedadmin", Admin: true},
	}
	for op, in := range refused {
		if _, err := r.Invoke(asUser("zeitgeist"), op, in); !errors.Is(err, ops.ErrForbidden) {
			t.Errorf("FAIL: %s by a forge agent: %v", op, err)
		}
	}
	// Re-marking a person as a forge agent is also making one.
	if _, err := r.Invoke(asUser("zeitgeist"), "account.agent", accountAgentIn{Name: "jva", Operator: "forge", Answerable: "dmestas"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a forge agent re-marked a person as a forge agent: %v", err)
	}
	// Re-inviting a pending site admin without the flag would clear it.
	if _, err := r.Invoke(asUser("dmestas"), "account.invite", accountInviteIn{Name: "pendingadmin", Admin: true}); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.invite", accountInviteIn{Name: "pendingadmin"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a forge agent re-invited a site admin without the flag: %v", err)
	}
	// The account-level ops a site admin runs against another account
	// are refused a forge agent when the target is a forge agent or
	// holds s: marking strips the flag, disabling and deleting lock
	// out, a reset or an address takes the account.
	if _, err := r.Invoke(asUser("dmestas"), "account.add", accountAddIn{Name: "pal", Password: "a-long-enough-password"}); err != nil {
		t.Fatal(err)
	}
	for _, c := range []struct {
		op string
		in any
	}{
		{"account.agent", accountAgentIn{Name: "dmestas", Operator: "jva"}},
		{"account.agent", accountAgentIn{Name: "dmestas"}},
		{"account.agent", accountAgentIn{Name: "fbot"}},
		{"account.agent", accountAgentIn{Name: "fbot", Operator: "jva"}},
		{"account.delete", accountDeleteIn{Name: "dmestas"}},
		{"account.delete", accountDeleteIn{Name: "fbot"}},
		{"account.disable", accountDisableIn{Name: "dmestas", On: true}},
		{"account.disable", accountDisableIn{Name: "fbot", On: true}},
		{"account.reset", accountResetIn{Name: "dmestas"}},
		{"account.invite.withdraw", accountDeleteIn{Name: "pendingadmin"}},
		{"account.grant", accountGrantIn{Name: "fbot", Repo: "test", Role: "reader"}},
		{"account.revoke", accountRevokeIn{Name: "fbot", Repo: "test"}},
		{"account.caps", accountCapsIn{Account: "fbot", Repo: "test", Caps: "u"}},
	} {
		if _, err := r.Invoke(asUser("zeitgeist"), c.op, c.in); !errors.Is(err, ops.ErrForbidden) {
			t.Errorf("FAIL: %s %+v by a forge agent: %v", c.op, c.in, err)
		}
	}
	// session.end: a forge agent's reach is its own sessions, not
	// everyone's as s would otherwise give it.
	if _, err := r.Invoke(asUser("zeitgeist"), "session.end", sessionEndIn{Account: "dmestas"}); err == nil {
		t.Error("FAIL: a forge agent signed a site admin out everywhere")
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "session.end", sessionEndIn{Account: "zeitgeist"}); err != nil {
		t.Errorf("FAIL: a forge agent could not end its own sessions: %v", err)
	}
	// Raising a site admin's letters cannot lock them out: a grant and
	// a caps write that adds pass; one that drops a letter is refused.
	publisher := oltp.Publisher.Caps()
	if _, err := db.SetCapsGuarded("dmestas", "zeitgeist", "test", "a"+publisher); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.grant", accountGrantIn{Name: "dmestas", Repo: "test", Role: "reader"}); err != nil {
		t.Errorf("FAIL: a forge agent could not raise a site admin: %v", err)
	}
	// A grant replaces the string: reader over a site admin's a drops
	// a letter and is refused; admin over reader raises and passes.
	if err := db.Grant("admin2", "test", oltp.RepoAdmin); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.grant", accountGrantIn{Name: "admin2", Repo: "test", Role: "reader"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a forge agent granted reader over a site admin's a: %v", err)
	}
	if c, _ := db.CapsOf("admin2", "test"); c != "a" {
		t.Errorf("FAIL: admin2 on test reads %q after the refused grant", c)
	}
	if err := db.Grant("admin2", "test", oltp.Read); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.grant", accountGrantIn{Name: "admin2", Repo: "test", Role: "admin"}); err != nil {
		t.Errorf("FAIL: a forge agent could not grant admin over a site admin's reader: %v", err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.caps", accountCapsIn{Account: "dmestas", Repo: "test", Caps: publisher}); err != nil {
		t.Errorf("FAIL: a forge agent could not add a letter to a site admin: %v", err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.caps", accountCapsIn{Account: "dmestas", Repo: "test", Caps: "y"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a forge agent lowered a site admin's letters: %v", err)
	}
	if c, _ := db.CapsOf("dmestas", "test"); c != publisher {
		t.Errorf("FAIL: dmestas on test reads %q, want %q", c, publisher)
	}
	// A revoke lowers a site admin who holds something.
	if _, err := r.Invoke(asUser("zeitgeist"), "account.revoke", accountRevokeIn{Name: "dmestas", Repo: "test"}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: a forge agent revoked a site admin: %v", err)
	}
	if c, _ := db.CapsOf("dmestas", "test"); c != publisher {
		t.Errorf("FAIL: dmestas on test reads %q after the refused revoke", c)
	}
	// The same acts on a person who holds nothing pass, and a person
	// with s still takes them on a forge agent.
	if _, err := r.Invoke(asUser("zeitgeist"), "account.grant", accountGrantIn{Name: "pal", Repo: "test", Role: "reader"}); err != nil {
		t.Errorf("FAIL: a forge agent could not grant a person: %v", err)
	}
	if _, err := r.Invoke(asUser("zeitgeist"), "account.disable", accountDisableIn{Name: "pal", On: true}); err != nil {
		t.Errorf("FAIL: a forge agent could not disable a person: %v", err)
	}
	if _, err := r.Invoke(asUser("dmestas"), "account.disable", accountDisableIn{Name: "fbot", On: true}); err != nil {
		t.Errorf("FAIL: a site admin could not disable a forge agent: %v", err)
	}
	if a, _ := db.AccountByName("dmestas"); !a.Admin || a.Operator != "" || a.Disabled != "" {
		t.Errorf("FAIL: the site admin was altered: %+v", a)
	}
	if _, ok := db.Authenticate("dmestas", "a-long-enough-password"); !ok {
		t.Error("FAIL: the site admin's password is gone")
	}
	if a, ok := db.AccountByName("fbot"); !ok || a.Operator != "forge" {
		t.Error("FAIL: the other forge agent was altered")
	}
	if a, _ := db.AccountByName("pendingadmin"); !a.Admin {
		t.Error("FAIL: the pending admin lost the flag")
	} else {
		t.Log("ok: a forge agent with s passes account.add and the deed paths, never another forge agent or an s flag")
	}
}

// RFC 0026 review: a site admin by inheritance is held to every edge a
// forge agent is. It may not make a site admin or shut one.
func TestAnInheritedSiteAdminKeepsTheForgeAgentEdges(t *testing.T) {
	r, db, _ := agentCoreRig(t, 5)
	if err := db.NewAgent("dm-bot", "dmestas", -1); err != nil {
		t.Fatal(err)
	}
	if err := db.SetSiteOpt("dmestas", "dm-bot"); err != nil {
		t.Fatal(err)
	}
	if err := db.AddAccount("carol", "a-long-enough-password", true); err != nil {
		t.Fatal(err)
	}
	if _, err := r.Invoke(asUser("dm-bot"), "account.add", accountAddIn{Name: "mallory", Password: "a-long-enough-password", Admin: true}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: an inherited site admin made a site admin: %v", err)
	}
	if _, err := r.Invoke(asUser("dm-bot"), "account.disable", accountDisableIn{Name: "carol", On: true}); !errors.Is(err, ops.ErrForbidden) {
		t.Errorf("FAIL: an inherited site admin shut another site admin: %v", err)
	}
	t.Log("ok: the edges hold")
}