Revoke the reader's grants as their own grantors and read them back, alter its role only on drift, and report definer functions and views (review of hq issue 337)

A superuser's REVOKE acts as the table owner, so a grant to the reader made
by a role holding GRANT OPTION survived it while the report said revoked.
Each grant is now revoked under SET ROLE to its grantor, the grants are read
back, and anything still held is reported as a failure. The role was
altered every minute with its password in the statement, which a server
logging DDL would record and which could collide with a first query; it is
now altered only when an attribute, a setting or the password has drifted,
one ensure at a time. SECURITY DEFINER functions and views the reader may
use reach data without a grant on it, so every pass lists them. The README
says the name filter is a backstop and that the read-only session default
can be turned off by a statement.
This commit is contained in:
jochen
2026-10-08 22:01:43 +02:00
parent fb07d552a0
commit a643e28083
7 changed files with 431 additions and 92 deletions
@@ -111,3 +111,73 @@ func TestLiveTheReaderReadsWhatIsDeclaredAndNoSecret(t *testing.T) {
t.Fatal("a query ran while the reader can read a secret:", err)
}
}
// A grant to the reader made by a role holding GRANT OPTION — not the owner — is taken back as that
// role, and what still stands after a pass is a failure, not "revoked". SECURITY DEFINER functions and
// views the reader may use are reported.
func TestLiveAGrantByAnotherGrantorIsTakenBackAndDefinersAreSaid(t *testing.T) {
c := liveClient(t)
freshStore(t, c)
defer dropStore(c)
for _, sql := range []string{
"DROP ROLE IF EXISTS mesh_test_owner", "DROP ROLE IF EXISTS mesh_test_middle",
} {
if _, err := c.Query(ctx, "", sql); err != nil {
t.Fatal(sql, err)
}
}
defer func() {
_, _ = c.Query(ctx, "", "DROP DATABASE IF EXISTS "+Ident(liveStore)+" WITH (FORCE)")
_, _ = c.Query(ctx, "", "DROP ROLE IF EXISTS mesh_test_middle")
_, _ = c.Query(ctx, "", "DROP ROLE IF EXISTS mesh_test_owner")
}()
for _, sql := range []string{
"CREATE ROLE mesh_test_owner", "CREATE ROLE mesh_test_middle",
} {
if _, err := c.Query(ctx, "", sql); err != nil {
t.Fatal(sql, err)
}
}
for _, sql := range []string{
"ALTER TABLE notes OWNER TO mesh_test_owner",
"GRANT SELECT ON notes TO mesh_test_middle WITH GRANT OPTION",
"SET ROLE mesh_test_middle; GRANT SELECT ON notes TO " + Ident(Reader) + "; GRANT SELECT (body) ON notes TO " + Ident(Reader) + "; RESET ROLE",
"CREATE FUNCTION peek() RETURNS bytea LANGUAGE sql SECURITY DEFINER AS 'SELECT private FROM vault LIMIT 1'",
"CREATE VIEW plain_names AS SELECT name FROM vault",
"GRANT SELECT ON plain_names TO " + Ident(Reader),
} {
if _, err := c.Query(ctx, liveStore, sql); err != nil {
t.Fatal(sql, err)
}
}
var mine DatabaseReport
for _, db := range c.ReconcileReader(ctx, Declarations{}, "").Databases {
if db.Database == liveStore {
mine = db
}
}
if mine.Error != "" {
t.Fatal("left standing:", mine.Error)
}
if columnReadable(t, c, liveStore, "notes", "body") {
t.Fatal("the other grantor's grant survived")
}
if strings.Join(mine.Definers, ",") != "public.peek()" || strings.Join(mine.Views, ",") != "" {
t.Fatalf("definers %v, views %v", mine.Definers, mine.Views)
}
// A second pass finds nothing to take back.
for _, db := range c.ReconcileReader(ctx, Declarations{}, "").Databases {
if db.Database == liveStore && (len(db.Revoked) > 0 || db.Error != "") {
t.Fatalf("%+v", db)
}
}
// A view granted to PUBLIC is said.
if _, err := c.Query(ctx, liveStore, "GRANT SELECT ON plain_names TO PUBLIC"); err != nil {
t.Fatal(err)
}
for _, db := range c.ReconcileReader(ctx, Declarations{}, "").Databases {
if db.Database == liveStore && strings.Join(db.Views, ",") != "public.plain_names" {
t.Fatalf("views %v", db.Views)
}
}
}