Report as revoked only what the read-back shows gone, and take back what the reader passed on (review of hq issue 337)
A reader given a privilege WITH GRANT OPTION that passed it on made the plain revoke fail with dependent privileges exist; the pass still listed the table as revoked and logged it every minute. The pass now retries such a change with CASCADE, which reaches only grants that came from the reader, reads the grants back, reports as revoked only what is gone, and says what is still held and why when anything is.
This commit is contained in:
@@ -181,3 +181,51 @@ func TestLiveAGrantByAnotherGrantorIsTakenBackAndDefinersAreSaid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The reader was given a table WITH GRANT OPTION and passed it on. A plain revoke then fails with
|
||||
// "dependent privileges exist" and changes nothing; the pass must take back the reader's grant and
|
||||
// what the reader passed on (CASCADE reaches only grants that came from the reader), report as
|
||||
// revoked only what the read-back shows gone, and say what is still held and why if anything is.
|
||||
func TestLiveAGrantTheReaderPassedOnIsTakenBack(t *testing.T) {
|
||||
c := liveClient(t)
|
||||
freshStore(t, c)
|
||||
defer dropStore(c)
|
||||
defer func() { _, _ = c.Query(ctx, "", "DROP ROLE IF EXISTS mesh_test_onward") }()
|
||||
if _, err := c.Query(ctx, "", "DROP ROLE IF EXISTS mesh_test_onward; CREATE ROLE mesh_test_onward"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, sql := range []string{
|
||||
"REVOKE ALL ON vault FROM " + Ident(Reader),
|
||||
"GRANT SELECT ON notes TO " + Ident(Reader) + " WITH GRANT OPTION",
|
||||
"SET ROLE " + Ident(Reader) + "; GRANT SELECT ON notes TO mesh_test_onward; RESET ROLE",
|
||||
} {
|
||||
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.Fatalf("the reader still reads notes, and the pass said revoked %v", mine.Revoked)
|
||||
}
|
||||
if strings.Join(mine.Revoked, ",") != "public.notes" {
|
||||
t.Fatalf("revoked %v", mine.Revoked)
|
||||
}
|
||||
r, err := c.Query(ctx, liveStore, "SELECT has_table_privilege('mesh_test_onward', 'notes', 'SELECT')")
|
||||
if err != nil || cell(r.Rows[0], 0) != "f" {
|
||||
t.Fatal("what the reader passed on survived:", r.Rows, err)
|
||||
}
|
||||
// The next pass finds nothing held, and says nothing revoked.
|
||||
for _, db := range c.ReconcileReader(ctx, Declarations{}, "").Databases {
|
||||
if db.Database == liveStore && (len(db.Revoked) > 0 || db.Error != "") {
|
||||
t.Fatalf("%+v", db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user