Skip to content

Resolve the Dag's team when authorizing a Dag found by lookup - #70893

Open
potiuk wants to merge 5 commits into
apache:mainfrom
potiuk:resolve-the-dags-team-when-authorizing-by-resolved-dag
Open

Resolve the Dag's team when authorizing a Dag found by lookup#70893
potiuk wants to merge 5 commits into
apache:mainfrom
potiuk:resolve-the-dags-team-when-authorizing-by-resolved-dag

Conversation

@potiuk

@potiuk potiuk commented Aug 1, 2026

Copy link
Copy Markdown
Member

Two authorization checks build DagDetails(id=dag_id) without team_name:

  • materialize_asset, where the Dag is resolved from the asset rather than
    named by the caller;
  • the XCom-specific check in wait_dag_run_until_finished.

Every other call site resolves the team with DagModel.get_team_name and passes
it -- security.py:194, security.py:235, security.py:868,
import_error.py:314, services/public/task_instances.py:405.

Why it matters

A team-aware auth manager distinguishes a team-scoped Dag from a global one by
that field. The Keycloak manager, for instance, selects the resource it asks
about from it -- DAG:<team> when set, plain DAG when not -- so omitting it
asks about a differently-scoped resource than the one being acted on.

wait_dag_run_until_finished is the clearer illustration: its route dependency
already resolves the team for the RUN check, so the two checks in the same
handler disagreed about the scope of the same Dag.

Approach

Resolve the team at both sites with DagModel.get_team_name(dag_id, session=session),
reusing the request session rather than opening a new one.

After this, grep -rn "DagDetails(id=" airflow-core/src returns no call site
without team_name.

Test plan

  • test_authorizes_against_the_dags_team -- parametrized over a team-scoped
    Dag and a global one; asserts the DagDetails reaching the auth manager
    carries the resolved team
  • Verified against unmodified code: the team-scoped case fails, the
    global case passes either way -- so the test pins the resolution, not the
    plumbing
  • test_assets.py -- 178 passed
  • test_dag_run.py -- 358 passed; the 2 failures in
    TestBulkClearDagRuns / TestBulkDagRuns (UNIQUE constraint failed: team.name) are pre-existing and reproduce identically on unmodified
    main
  • ruff check / ruff format clean
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 5 (1M context)

Generated-by: Claude Opus 5 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

@potiuk
potiuk requested a review from ephraimbuddy as a code owner August 1, 2026 03:20
@potiuk potiuk added this to the Airflow 3.3.1 milestone Aug 1, 2026
@potiuk potiuk added the backport-to-v3-3-test Backport to v3-3-test label Aug 1, 2026
@boring-cyborg boring-cyborg Bot added the area:API Airflow's REST/HTTP API label Aug 1, 2026

@SameerMesiah97 SameerMesiah97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. The test could be improved though.

@amoghrajesh amoghrajesh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a later PR, but worth a guardrail like a DagDetails.for_dag(dag_id, *, session) constructor that always resolves the team

Two authorization checks build DagDetails(id=dag_id) without team_name:
materialize_asset, where the Dag is resolved from the asset, and the
XCom-specific check in wait_dag_run_until_finished. Every other call site
passes the team, resolved with DagModel.get_team_name.

A team-aware auth manager distinguishes a team-scoped Dag from a global one by
that field, so omitting it asks about a differently-scoped resource than the
one being acted on. In wait_dag_run_until_finished the route dependency already
resolves the team for its RUN check, so the two checks in the same handler
disagreed.

Resolve the team at both sites, reusing the request session.
@potiuk
potiuk force-pushed the resolve-the-dags-team-when-authorizing-by-resolved-dag branch from 20840db to 87dcc78 Compare August 3, 2026 23:12
@potiuk

potiuk commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Both review threads addressed, and the second call site now has its own regression test.

On the DagDetails.for_dag(dag_id, *, session) idea — I agree with it, and I think the diagnosis behind it is the important part: these two sites drifted precisely because DagDetails(id=...) makes the unscoped form the easy one to write and the scoped form something you have to remember. A constructor that always resolves the team turns that around.

Not in this PR though. It is milestoned for 3.3.1, and changing how DagDetails is constructed across the codebase is a much wider blast radius than the two-line fix this is — exactly the kind of change that should go to main on its own and be reviewed on its own merits, not ride in on a backport. Happy to see it done separately.


Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting

The existing wait-endpoint test uses a Dag with no team, where the resolved and
unresolved forms are indistinguishable, so nothing caught the second check asking
about a differently-scoped resource than the route dependency did.
@potiuk
potiuk force-pushed the resolve-the-dags-team-when-authorizing-by-resolved-dag branch from 87dcc78 to 9ebd025 Compare August 3, 2026 23:34
@potiuk

potiuk commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Both threads addressed and resolved. The missing regression test for wait_dag_run_until_finished is in — worth noting why it was missing: the existing test asserts on the DagDetails passed to the XCom check, but against a team-less Dag, where DagDetails(id=X) and DagDetails(id=X, team_name=None) are equal, so it passed identically with and without the fix. The new test parametrizes over a team-scoped Dag. Mocks now carry spec/autospec.

@amoghrajesh @SameerMesiah97


Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting

@amoghrajesh amoghrajesh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK by me, but if someone like @vincbeck / @o-nikolas could review, that would be nice.

Comment thread airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py Outdated
Comment thread airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py Outdated
potiuk and others added 3 commits August 4, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API backport-to-v3-3-test Backport to v3-3-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants