Skip to content
quarry Artifact [33e4810032]
Login

Artifact [33e4810032]

Artifact 33e4810032f048518cc63ba78ed7c8eb9b81dff51e08fbebd83146d6445d96fb:


package main

import (
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"testing"

	"github.com/ncruces/go-sqlite3/driver"

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

// AN AGENT'S PASSWORD OPENS NO DOOR; ITS TOKEN OPENS EACH ONE IT IS
// SCOPED FOR (RFC 0021, cutover). The agent here still holds the
// password it had before marking cleared one, as a row from before
// rung 3 does, so the refusal is the doors' and not a missing hash.
func TestAgentPasswordRefusedAtTheHTTPDoors(t *testing.T) {
	path := filepath.Join(t.TempDir(), "oltp.db")
	db, err := oltp.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	rg := &registrar{odb: db, deps: &api.Deps{Repos: api.NewRepos(nil)}, repoQuota: 25}
	const pw = "pw12345678"
	for _, who := range []string{"jan", "jan-bot"} {
		if err := db.AddAccount(who, pw, false); err != nil {
			t.Fatal(err)
		}
	}
	if err := db.AddRepo("vault", t.TempDir(), "", oltp.Private); err != nil {
		t.Fatal(err)
	}
	for _, who := range []string{"jan", "jan-bot"} {
		if err := db.SetCaps(who, "vault", "oi"); err != nil {
			t.Fatal(err)
		}
	}
	rg.refreshPrivate()
	raw, err := driver.Open("file:" + path)
	if err != nil {
		t.Fatal(err)
	}
	defer raw.Close()
	if _, err := raw.Exec(`UPDATE account SET operator = 'jan' WHERE name = 'jan-bot'`); err != nil {
		t.Fatal(err)
	}
	syncTok, _, err := db.MintToken("jan-bot", oltp.TokenSpec{Name: "push", Scopes: []string{oltp.ScopeSync}})
	if err != nil {
		t.Fatal(err)
	}
	apiTok, _, err := db.MintToken("jan-bot", oltp.TokenSpec{Name: "api", Scopes: []string{oltp.ScopeAPI}})
	if err != nil {
		t.Fatal(err)
	}

	doors := &tokenDoors{db: db}
	// main.go's byPassword with accounts: the password is Authenticate's.
	byPassword := func(repo, user, pass string, need ops.Auth) api.Decision {
		if _, ok := db.Authenticate(user, pass); !ok {
			return api.Denied
		}
		return api.Allowed
	}
	apiDoor := doors.verify(oltp.ScopeAPI, byPassword)
	webDoor := doors.verify(oltp.ScopeWeb, byPassword)
	basic := func(user, secret string) *http.Request {
		req := httptest.NewRequest(http.MethodGet, "/xfer/vault", nil)
		req.SetBasicAuth(user, secret)
		return req
	}

	for _, c := range []struct {
		door string
		got  bool
		want bool
	}{
		{"api password", apiDoor("vault", "jan-bot", pw, ops.Token) == api.Allowed, false},
		{"web password", webDoor("vault", "jan-bot", pw, ops.Token) == api.Allowed, false},
		{"xfer front password", rg.SyncReadGate(basic("jan-bot", pw), "vault", ""), false},
		{"sync identity password", ops.User(syncIdentityCtx(basic("jan-bot", pw), doors)) != "", false},
		{"api token", apiDoor("vault", "jan-bot", apiTok, ops.Token) == api.Allowed, true},
		{"xfer front token", rg.SyncReadGate(basic("jan-bot", syncTok), "vault", ""), true},
		{"sync identity token", ops.User(syncIdentityCtx(basic("jan-bot", syncTok), doors)) == "jan-bot", true},
		{"person api password", apiDoor("vault", "jan", pw, ops.Token) == api.Allowed, true},
		{"person xfer front password", rg.SyncReadGate(basic("jan", pw), "vault", ""), true},
	} {
		if c.got != c.want {
			t.Errorf("FAIL: %s: admitted = %v, want %v", c.door, c.got, c.want)
		} else {
			t.Logf("ok: %s: admitted = %v", c.door, c.got)
		}
	}
}