This is an automated email from the ASF dual-hosted git repository.

vincbeck pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new a128d6930ef Create documentation about adding `access_control` to 
`Asset` object (#66949)
a128d6930ef is described below

commit a128d6930ef2378a3f9638ed171bdcfae426a633
Author: Vincent <[email protected]>
AuthorDate: Tue May 26 12:10:13 2026 -0700

    Create documentation about adding `access_control` to `Asset` object 
(#66949)
---
 .../docs/authoring-and-scheduling/assets.rst       | 68 +++++++++++++++++-----
 airflow-core/docs/core-concepts/multi-team.rst     | 56 ++++++++++++++----
 2 files changed, 100 insertions(+), 24 deletions(-)

diff --git a/airflow-core/docs/authoring-and-scheduling/assets.rst 
b/airflow-core/docs/authoring-and-scheduling/assets.rst
index 9fbb232cbb3..74a22b65e51 100644
--- a/airflow-core/docs/authoring-and-scheduling/assets.rst
+++ b/airflow-core/docs/authoring-and-scheduling/assets.rst
@@ -404,10 +404,10 @@ As mentioned in :ref:`Fetching information from 
previously emitted asset events<
             events = inlet_events[AssetAlias("example-alias")]
             last_row_count = events[-1].extra["row_count"]
 
-.. _asset_allow_producer_teams:
+.. _asset_access_control:
 
-Cross-team asset event filtering with ``allow_producer_teams``
---------------------------------------------------------------
+Cross-team asset event filtering with ``access_control``
+--------------------------------------------------------
 
 .. versionadded:: 3.3.0
 
@@ -415,40 +415,82 @@ When :doc:`Multi-Team mode </core-concepts/multi-team>` 
is enabled, asset events
 membership. By default, a consuming Dag only receives asset events produced by 
Dags within the same team
 or by global (teamless) Dags. This prevents unintended cross-team triggers.
 
-To allow specific other teams to produce events that trigger your Dag, use the 
``allow_producer_teams`` parameter
-on the ``Asset`` definition:
+To configure cross-team access, use the ``access_control`` parameter on the 
``Asset`` definition with an
+``AssetAccessControl`` instance:
 
 .. code-block:: python
 
-    from airflow.sdk import Asset
+    from airflow.sdk import Asset, AssetAccessControl
 
     shared_data = Asset(
         name="my_data",
         uri="s3://bucket/shared/data.csv",
-        allow_producer_teams=["team_analytics", "team_ml"],
+        access_control=AssetAccessControl(
+            producer_teams=["team_analytics", "team_ml"],
+        ),
     )
 
 In this example, asset events produced by Dags belonging to ``team_analytics`` 
or ``team_ml`` will be
 accepted by any consuming Dag that schedules on ``shared_data``, in addition 
to events from the consuming
 Dag's own team.
 
+``AssetAccessControl`` parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``AssetAccessControl`` class accepts the following parameters:
+
+- **producer_teams** (``list[str]``, default ``[]``): List of team names 
allowed to produce events
+  consumed by this asset's consumers, in addition to the consumer's own team.
+- **allow_global** (``bool``, default ``True``): Whether teamless (global) Dag 
producers can trigger
+  consumers of this asset. When set to ``False``, only Dags with an explicit 
team association
+  (same team or listed in ``producer_teams``) can trigger consumers.
+
+Blocking global producers
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, global (teamless) Dags can trigger any consumer. In strict team 
isolation scenarios, you
+may want to block teamless producers:
+
+.. code-block:: python
+
+    from airflow.sdk import Asset, AssetAccessControl
+
+    strict_data = Asset(
+        name="strict_data",
+        uri="s3://bucket/strict/data.csv",
+        access_control=AssetAccessControl(
+            producer_teams=["team_analytics"],
+            allow_global=False,
+        ),
+    )
+
+With ``allow_global=False``, only Dags belonging to the consumer's own team or 
to ``team_analytics`` can
+trigger consumers of ``strict_data``. Teamless Dag producers are blocked.
+
+.. note::
+
+   The ``allow_global`` flag only affects Dag producers. Teamless API users 
are always restricted to
+   triggering teamless consumers only, regardless of this setting.
+
 Default behavior
 ~~~~~~~~~~~~~~~~
 
-When ``allow_producer_teams`` is not specified (or set to an empty list), the 
default same-team filtering applies.
-The rules depend on whether the producer and consumer have a team association:
+When ``access_control`` is not specified, a default ``AssetAccessControl()`` 
is used (empty
+``producer_teams`` and ``allow_global=True``). The rules depend on whether the 
producer and consumer
+have a team association:
 
 - **Both have the same team**: The event is always delivered.
 - **Producer has a team, consumer has a different team**: The event is blocked 
(unless the
-  producer's team is in the asset's ``allow_producer_teams``).
-- **Producer has no team (global Dag)**: The event is delivered to all 
consumers, regardless of
-  the consumer's team. Global Dags act as shared infrastructure that any team 
can depend on.
+  producer's team is in the asset's ``producer_teams``).
+- **Producer has no team (global Dag)**: The event is delivered to all 
consumers whose asset has
+  ``allow_global=True`` (the default). Global Dags act as shared 
infrastructure that any team can
+  depend on.
 - **Consumer has no team (global Dag)**: The consumer accepts events from any 
source,
   regardless of the producer's team. Teamless consumers act as shared 
infrastructure that any
   team can feed into.
 - **Neither has a team**: The event is delivered (both are global).
 
-When Multi-Team mode is disabled, ``allow_producer_teams`` is ignored and all 
asset events are delivered to all
+When Multi-Team mode is disabled, ``access_control`` is ignored and all asset 
events are delivered to all
 consuming Dags, preserving backward compatibility.
 
 Asset partitions
diff --git a/airflow-core/docs/core-concepts/multi-team.rst 
b/airflow-core/docs/core-concepts/multi-team.rst
index 88a99e2be40..eb6371701b6 100644
--- a/airflow-core/docs/core-concepts/multi-team.rst
+++ b/airflow-core/docs/core-concepts/multi-team.rst
@@ -517,26 +517,41 @@ Default Behavior
 
 By default, a consuming Dag only receives asset events from producers within 
the same team or from Dags with no team association, i.e. global Dags.
 
-Cross-Team Opt-In with ``allow_producer_teams``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Cross-Team Opt-In with ``access_control``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 To allow specific teams to produce events that trigger consumers on a given 
asset from another team, use the
-``allow_producer_teams`` parameter on the ``Asset`` definition:
+``access_control`` parameter on the ``Asset`` definition with an 
``AssetAccessControl`` instance:
 
 .. code-block:: python
 
-    from airflow.sdk import Asset
+    from airflow.sdk import Asset, AssetAccessControl
 
     shared_data = Asset(
         name="shared_data",
         uri="s3://bucket/shared/data.csv",
-        allow_producer_teams=["team_analytics", "team_ml"],
+        access_control=AssetAccessControl(
+            producer_teams=["team_analytics", "team_ml"],
+        ),
     )
 
 With this configuration, asset events from ``team_analytics`` or ``team_ml`` 
will be accepted by any
 consuming Dag that schedules on ``shared_data``, in addition to events from 
the consumer's own team.
 
-See :ref:`Cross-team asset event filtering with allow_producer_teams 
<asset_allow_producer_teams>` in the Assets
+To block global (teamless) Dag producers from triggering consumers, set 
``allow_global=False``:
+
+.. code-block:: python
+
+    strict_data = Asset(
+        name="strict_data",
+        uri="s3://bucket/strict/data.csv",
+        access_control=AssetAccessControl(
+            producer_teams=["team_analytics"],
+            allow_global=False,
+        ),
+    )
+
+See :ref:`Cross-team asset event filtering with access_control 
<asset_access_control>` in the Assets
 documentation for usage details and validation rules.
 
 Behavioral Rules
@@ -546,79 +561,98 @@ The following table describes the complete filtering 
logic:
 
 .. list-table::
    :header-rows: 1
-   :widths: 20 20 20 15 25
+   :widths: 15 15 18 14 13 25
 
    * - Producer
      - Consumer
-     - ``allow_producer_teams``
+     - ``producer_teams``
+     - ``allow_global``
      - Result
      - Reason
    * - Team A (DAG)
      - Team A
      - (any)
+     - (any)
      - ✅ Allowed
      - Same team
    * - Team A (DAG)
      - Team B
      - ``[]``
+     - (any)
      - ❌ Blocked
      - Different team, no opt-in
    * - Team A (DAG)
      - Team B
      - ``["team_a"]``
+     - (any)
      - ✅ Allowed
      - Cross-team opt-in
    * - (no team, DAG)
      - Team B
      - (any)
+     - ``True``
      - ✅ Allowed
-     - Global producer
+     - Global producer, allow_global is True
+   * - (no team, DAG)
+     - Team B
+     - (any)
+     - ``False``
+     - ❌ Blocked
+     - Global producer blocked by allow_global=False
    * - Team A (DAG)
      - (no team)
      - (any)
+     - (any)
      - ✅ Allowed
      - Teamless consumer accepts events from any DAG producer
    * - (no team, DAG)
      - (no team)
      - (any)
+     - (any)
      - ✅ Allowed
      - Both global
    * - Team A (API)
      - Team A
      - (any)
+     - (any)
      - ✅ Allowed
      - Same team
    * - Team A (API)
      - Team B
      - ``["team_a"]``
+     - (any)
      - ✅ Allowed
      - Cross-team opt-in
    * - Team A (API)
      - (no team)
      - (any)
+     - (any)
      - ✅ Allowed
      - Teamless consumer accepts events from any source
    * - (no team, API)
      - Team B
      - (any)
+     - (any)
      - ❌ Blocked
      - Teamless API user cannot trigger team-bound consumer
    * - (no team, API)
      - (no team)
      - (any)
+     - (any)
      - ✅ Allowed
      - Both global
 
 Key rules:
 
 - **Same team**: Always allowed.
-- **Global (teamless) DAG producer**: Triggers all consumers regardless of 
team.
+- **Global (teamless) DAG producer with** ``allow_global=True``: Triggers all 
consumers regardless of team.
+- **Global (teamless) DAG producer with** ``allow_global=False``: Blocked from 
triggering team-bound consumers.
 - **Teamless API user**: Can only trigger teamless consumers. Unlike a 
teamless DAG — which is
   deployed by a platform operator and intentionally shared — an API user 
without a team has no
   verified team affiliation, so their events are restricted to teamless 
consumers to
   prevent unscoped access to team-bound pipelines.
 - **Teamless consumer**: Accepts events from any source (DAG or API), 
regardless of team.
-- **Cross-team via** ``allow_producer_teams``: Allowed when the producer's 
team is listed in the asset's ``allow_producer_teams``.
+- **Cross-team via** ``producer_teams``: Allowed when the producer's team is 
listed in the asset's ``producer_teams``.
 - **Multi-Team disabled**: All filtering is skipped; existing behavior is 
preserved.
 
 API-Triggered Events

Reply via email to