Skip to content

Check warehouse session identity and query access - #281

Draft
jat255 wants to merge 3 commits into
jat255/xp65-databricks-readerfrom
jat255/xp65-catalog-security
Draft

Check warehouse session identity and query access#281
jat255 wants to merge 3 commits into
jat255/xp65-databricks-readerfrom
jat255/xp65-catalog-security

Conversation

@jat255

@jat255 jat255 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fifth of the catalog import stack (kata xp65). Stacked on #278. Ports the access half of catalog-security.R.

A warehouse decides who may read what, so commons asks with a query that returns no rows and reads the answer off the failure. The classification is what matters: an authorization refusal is stable, so it is cached per relation; a transient one is retried on the next touch; anything unrecognized is neither. The session snapshot is there for the same reason, since access was decided for one principal, role, and namespace, and if any of those move the answers no longer apply.

Semantic-model probing is the other half of that file and stays with the semantic model work (kata gcgj). ensure_queryable() ships with no caller: R reaches it from source_describe(), and Python has no first-touch surface until the retrieval work, so nothing resolves a relation label at query time yet.

Shared fixtures

Three behaviours were about to be implemented and asserted twice, so they move to tests/shared/: how a driver failure classifies (catalog-access-errors.json), which parts of a session identity a refusal reports (catalog-session-changed.json), and what construction reports when several named relations fail at once (catalog-access-precedence.json). Both suites read them. Field names travel rather than message text, since the wording belongs to each language.

R changes

Two defects, both found by writing the same contract a second time.

The session refusal named every field the check compares, which told a Databricks user that a role had changed on a backend that has no role to change. catalog_check_session_snapshot() now compares field by field and names only what differs, through a new catalog_session_changed_fields() and a label lookup for the wording. The behaviour delta is the message text; the condition class is unchanged, and no test matched on the old wording. The roxygen for data_source(), its .Rd, and the governance vignette carried the same over-broad claim and now scope the role snapshot to Snowflake.

catalog_require_queryable_relations() raised on the first relation it could not read, which discarded any missing names it had already collected: the caller fixed a typo and only then learned about the next problem. It now skips the relations the listing never reported, probes the rest, and reports every missing name before any refusal. The delta is in the failure path only, and it can cost extra round trips there, since a refusal no longer stops the loop. A successful construction probes exactly what it did before.

Blast radius: both functions are reached only from data_source_connection(), once per warehouse branch, plus catalog_check_session() for the session check, which source_query(), source_describe(), catalog_search(), and the semantic model path all go through. Neither signature changed.

Why it is safe: test-catalog-security.R (102) and test-data-source.R (66, 10 skipped) pass. Both new fixture runners were confirmed to bite, by restoring the abort-on-first-refusal behaviour and watching five R cases fail, and by flipping an expected outcome in each suite. I did not run the full R suite, which hangs for about ten minutes until #268 lands.

Worth your scrutiny: the precedence change is a behaviour change in R rather than a port artefact, and whether reporting everything at once is worth the extra probes on the failure path.

Deleted R tests: "warehouse access errors are classified conservatively" is now the fixture runner plus one R-only case for NA_character_, which JSON cannot spell. Nothing it asserted was dropped.

pkg-r/tests/testthat/fixtures/shared/ is generated by scripts/sync-shared-fixtures.sh; skip it.

Verification

707 Python tests, ruff and pyrefly clean.

@jat255 jat255 added this to the py-M2: data layer milestone Sep 4, 2026
@jat255 jat255 added needs-manual-review Agent-created work that needs a human review py Affects the Python implementation labels Sep 4, 2026
A warehouse decides who may read what, so commons asks with a zero-row
query and classifies the failure: an authorization refusal is stable and
is cached per relation, a transient one retries on the next touch, and an
unrecognized one is neither. The session snapshot pins the principal,
roles, and namespace those answers were decided for, and a refusal names
which of them moved.

Every relation the listing reported is probed before anything is raised,
so a name the caller got wrong and a relation they cannot read are
reported in one pass rather than one round trip each.

The check pairs the selection with the listing by label, which meant the
two had to agree on one. They did not for an entry naming a bare table:
the warehouse qualifies it from the connection's namespace, so the
registry now records the relation's own label rather than the selector's.
…relation

Two defects the Python implementation of the same contract exposed. The
session refusal listed every field the check compares, which told a
Databricks user a role had changed on a backend that has none; it now
names the fields that differ, and the documentation and the governance
vignette scope the role snapshot to Snowflake. And an access refusal on
one named relation discarded the missing names already found, so the
caller fixed a typo only to be told about the next problem on the next
attempt; every relation the listing reported is now probed before
anything is raised.
…dence

Three behaviours both packages have to agree on, each implemented twice
and asserted twice until now: how a driver failure classifies, which
parts of a session identity a refusal reports, and what construction
reports when several named relations fail at once. They move to
tests/shared/ and both suites read them. Field names travel rather than
message text, since the wording belongs to each language.
@jat255
jat255 force-pushed the jat255/xp65-catalog-security branch from f2baa36 to e76c9ea Compare September 5, 2026 04:58
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Preview deployed to Connect (dogfood.team.pct.posit.it): https://dogfood.team.pct.posit.it/connect/#/apps/d7a36cae-8f27-448b-a478-61b81fbe3942/draft/367962

Deployed from commit e76c9ea.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Preview deployed to Connect (dogfood.team.pct.posit.it): https://dogfood.team.pct.posit.it/connect/#/apps/d7a36cae-8f27-448b-a478-61b81fbe3942/draft/367965

Deployed from commit e76c9ea.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Preview deployed to Connect (connect.staging.pct.posit.it): https://connect.staging.pct.posit.it/connect/#/apps/ad662e1b-5048-4acc-9ad7-f9478c92274e/draft/2592

Deployed from commit e76c9ea.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Preview deployed to Connect (connect.staging.pct.posit.it): https://connect.staging.pct.posit.it/connect/#/apps/ad662e1b-5048-4acc-9ad7-f9478c92274e/draft/2593

Deployed from commit e76c9ea.

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

Labels

needs-manual-review Agent-created work that needs a human review py Affects the Python implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant