package oltp
import (
"strings"
"testing"
)
// siteRig is agentRig with zeitgeist as it stands on zeitforge: a forge
// agent holding the site-admin flag, root answerable.
func siteRig(t *testing.T) *DB {
t.Helper()
db := agentRig(t)
if err := db.AddAccount("zeitgeist", "a-long-enough-password", true); err != nil {
t.Fatal(err)
}
if err := db.SetForgeAgent("zeitgeist", "root"); err != nil {
t.Fatal(err)
}
if a, _ := db.AccountByName("zeitgeist"); !a.Admin || a.Operator != forgeOperator {
t.Fatalf("rig: zeitgeist reads %+v", a)
}
if err := db.NewForgeAgent("fbot", "root"); err != nil {
t.Fatal(err)
}
return db
}
// A site token is a forge agent's that holds the flag, and nobody
// else's: not a person's (a site admin's included), not a person's
// agent, not a forge agent without the flag. It needs a door to act
// at and reaches the whole forge, so it names no repositories.
func TestASiteTokenIsMintedOnlyForAFlaggedForgeAgent(t *testing.T) {
db := siteRig(t)
for _, tc := range []struct {
who string
spec TokenSpec
}{
{"root", TokenSpec{Name: "t", Scopes: []string{ScopeAPI, ScopeSite}}},
{"dmestas", TokenSpec{Name: "t", Scopes: []string{ScopeAPI, ScopeSite}}},
{"dmestas-coder", TokenSpec{Name: "t", Scopes: []string{ScopeAPI, ScopeSite}}},
{"fbot", TokenSpec{Name: "t", Scopes: []string{ScopeAPI, ScopeSite}}},
{"zeitgeist", TokenSpec{Name: "t", Scopes: []string{ScopeSite}}},
{"zeitgeist", TokenSpec{Name: "t", Scopes: []string{ScopeSync, ScopeSite}}},
{"zeitgeist", TokenSpec{Name: "t", Scopes: []string{ScopeAPI, ScopeSite}, Repos: []string{"pub"}}},
} {
_, _, err := db.MintToken(tc.who, tc.spec)
if !IsRefusal(err) {
t.Errorf("FAIL: %s minted %v repos %v: %v", tc.who, tc.spec.Scopes, tc.spec.Repos, err)
continue
}
t.Logf("ok: %s %v refused: %v", tc.who, tc.spec.Scopes, err)
}
for _, scopes := range [][]string{{ScopeAPI, ScopeSite}, {ScopeWeb, ScopeSite}, {ScopeSync, ScopeAPI, ScopeWeb, ScopeSite}} {
if _, tok, err := db.MintToken("zeitgeist", TokenSpec{Name: strings.Join(scopes, "+"), Scopes: scopes}); err != nil || !tok.Has(ScopeSite) {
t.Errorf("FAIL: zeitgeist could not mint %v: %v", scopes, err)
}
}
}
// The flag reaches a site token at forge scope, asked on every call:
// dropping the flag ends it at once, and it never reaches a
// repository. A forge agent's token without the site scope carries it
// too: the account's flag decides, not a second token.
func TestASiteTokenCarriesTheFlagLiveAndOnlyAtForgeScope(t *testing.T) {
db := siteRig(t)
_, site, err := db.MintToken("zeitgeist", TokenSpec{Name: "site", Scopes: []string{ScopeAPI, ScopeSite}})
if err != nil {
t.Fatal(err)
}
_, plain, err := db.MintToken("zeitgeist", TokenSpec{Name: "plain", Scopes: []string{ScopeAPI}})
if err != nil {
t.Fatal(err)
}
if !db.SiteToken(site) || !db.TokenCaps(site, fleetRepo).HasAny("s") {
t.Errorf("FAIL: the site token does not carry the flag at forge scope")
}
if !db.SiteToken(plain) || !db.TokenCaps(plain, fleetRepo).HasAny("s") {
t.Errorf("FAIL: a forge agent's token without the site scope does not carry its flag")
}
for _, repo := range []string{"pub", "priv"} {
if got := db.TokenCaps(site, repo); got.HasAny("s") || got.HasAny("a") {
t.Errorf("FAIL: the site token holds %q on %s, where zeitgeist holds no grant", got, repo)
}
}
if db.TokenCaps(site, "priv").HasAny("gjoh2r") {
t.Errorf("FAIL: the site token reads a private repository it holds no grant on")
}
if _, err := db.w.Exec(`UPDATE account SET admin = 0 WHERE name = 'zeitgeist'`); err != nil {
t.Fatal(err)
}
if db.SiteToken(site) || db.TokenCaps(site, fleetRepo).HasAny("s") {
t.Errorf("FAIL: the site token kept the flag after it was dropped")
} else {
t.Log("ok: dropping the flag ends the site token's s at once")
}
}
// A caller that holds the token row it connected with (a stream) is
// answered from the token as it is now: revoked is revoked.
func TestASiteTokenRowHeldSinceConnectIsReadAgain(t *testing.T) {
d := siteRig(t)
_, tok, err := d.MintToken("zeitgeist", TokenSpec{Name: "s", Scopes: []string{ScopeAPI, ScopeSite}})
if err != nil {
t.Fatal(err)
}
if !d.SiteToken(tok) {
t.Fatal("a live site token is not a site token")
}
if err := d.RevokeToken("zeitgeist", tok.ID); err != nil {
t.Fatal(err)
}
if d.SiteToken(tok) {
t.Error("FAIL: a revoked site token, asked about by the row held from before, still carries the flag")
}
}