Skip to content

Inherited grants are silently lost when blue-green swaps databases #641

Description

@noel

Summary

After a dbt-coves blue-green run, every inherited grant on the production
database is gone. Grants held directly on the database and on its schemas are
restored correctly, so the failure is partial and silent: the run reports
success, the database and its schemas stay reachable, and reads fail.

In practice this means a reader role that had SELECT on all tables and views
in the database — granted once via an inherited grant — can still see the
database and its schemas after the swap, but can no longer select from anything
in it. Because the role retains USAGE but loses SELECT, Snowflake omits the
objects from SHOW/INFORMATION_SCHEMA entirely for that role, so it presents
to users as "the tables and views disappeared" rather than as a privilege error.

Nothing in the run's output indicates it happened.

Root cause

CloneDB.clone_database_grants rebuilds database-level grants on the staging
database by reading them off production and re-issuing them:

grants_sql_stg_1 = f"""show grants on database {blue_database}"""
dict_cursor.execute(grants_sql_stg_1)
grants = dict_cursor.fetchall()
for grant in grants:
    grant_sql = (
        f"GRANT {grant['privilege']} ON {grant['granted_on']} {green_database} "
        f"TO ROLE {grant['grantee_name']};"
    )

SHOW GRANTS ON DATABASE <db> returns only the privileges held on the database
object itself — OWNERSHIP and USAGE. It does not return inherited
grants on the tables, views and dynamic tables inside the database. Those are
visible only from the grantee side, via SHOW GRANTS TO ROLE <role>, where they
appear as granted_on = TABLE|VIEW|DYNAMIC_TABLE with is_inherited = true
and inherited_from = DATABASE.

Since the copy loop cannot see them, it cannot re-issue them. The staging
database is built without them, and after
alter database <prod> swap with <staging> the production name points at an
object that never had them. The original grants belonged to the old database
object and are dropped along with it.

clone_database_schemas has the same blind spot at schema level, and neither
method queries SHOW FUTURE GRANTS, so future grants are lost the same way.

Reproduction

-- given a reader role holding an inherited read grant on a database
grant usage on database my_db to role reader_role;
grant usage on inherited schemas in database my_db to role reader_role;
grant select on inherited tables in database my_db to role reader_role;
grant select on inherited views in database my_db to role reader_role;

Confirm the grants are in place, and note that they are invisible to the query
the copy loop relies on:

show grants to role reader_role;      -- inherited SELECT rows present
show grants on database my_db;        -- only OWNERSHIP + USAGE; no SELECT rows

Then run dbt-coves blue-green against my_db and re-check:

show grants to role reader_role;      -- inherited SELECT rows gone

USAGE on the database and on each schema is intact; the SELECT grants are
not. The run exits 0.

Notes

Observed on dbt-coves 1.12.1; clone_database_grants on main is identical
at the time of writing.

Inherited grants require FEATURE_RBAC_INHERITED_GRANTS. They are an
attractive pairing with blue-green precisely because they are durable single
records that survive object creation inside the database, which is what makes
losing them on swap surprising.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpythonPull requests that update python codetriaged: noHasn't been approved for future implementation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions