Skip to content

Commit 9abdb66

Browse files
committed
Fix missing and incorrect endpoints in the API permission reference
1 parent 935bac0 commit 9abdb66

5 files changed

Lines changed: 263 additions & 41 deletions

File tree

‎airflow-core/docs/security/api_permissions_ref.rst‎

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,15 @@ source code so it stays up to date as endpoints are added or changed.
103103
- ``Asset``
104104
- ``GET``
105105
* - ``DELETE``
106-
- ``/api/v2/assets/{asset_id}/state-store/{key:path}``
106+
- ``/api/v2/assets/{asset_id}/state-store/{key}``
107107
- ``Asset``
108108
- ``DELETE``
109109
* - ``GET``
110-
- ``/api/v2/assets/{asset_id}/state-store/{key:path}``
110+
- ``/api/v2/assets/{asset_id}/state-store/{key}``
111111
- ``Asset``
112112
- ``GET``
113113
* - ``PUT``
114-
- ``/api/v2/assets/{asset_id}/state-store/{key:path}``
114+
- ``/api/v2/assets/{asset_id}/state-store/{key}``
115115
- ``Asset``
116116
- ``PUT``
117117
* - ``GET``
@@ -130,8 +130,28 @@ source code so it stays up to date as endpoints are added or changed.
130130
- ``/api/v2/backfills``
131131
- ``DAG.RUN``
132132
- ``POST``
133+
* - ``POST``
134+
- ``/api/v2/backfills/dry_run``
135+
- ``DAG.RUN``
136+
- ``POST``
137+
* - ``GET``
138+
- ``/api/v2/backfills/{backfill_id}``
139+
- ``DAG.RUN``
140+
- ``GET``
133141
* - ``PUT``
134-
- ``/api/v2/backfills``
142+
- ``/api/v2/backfills/{backfill_id}/cancel``
143+
- ``DAG.RUN``
144+
- ``PUT``
145+
* - ``GET``
146+
- ``/api/v2/backfills/{backfill_id}/dag_runs``
147+
- ``DAG.RUN``
148+
- ``GET``
149+
* - ``PUT``
150+
- ``/api/v2/backfills/{backfill_id}/pause``
151+
- ``DAG.RUN``
152+
- ``PUT``
153+
* - ``PUT``
154+
- ``/api/v2/backfills/{backfill_id}/unpause``
135155
- ``DAG.RUN``
136156
- ``PUT``
137157
* - ``GET``
@@ -379,19 +399,19 @@ source code so it stays up to date as endpoints are added or changed.
379399
- ``DAG.TASK_INSTANCE``
380400
- ``GET``
381401
* - ``DELETE``
382-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
402+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
383403
- ``DAG.TASK_INSTANCE``
384404
- ``DELETE``
385405
* - ``GET``
386-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
406+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
387407
- ``DAG.TASK_INSTANCE``
388408
- ``GET``
389409
* - ``PATCH``
390-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
410+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
391411
- ``DAG.TASK_INSTANCE``
392412
- ``PUT``
393413
* - ``PUT``
394-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
414+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
395415
- ``DAG.TASK_INSTANCE``
396416
- ``PUT``
397417
* - ``GET``
@@ -411,15 +431,15 @@ source code so it stays up to date as endpoints are added or changed.
411431
- ``DAG.XCOM``
412432
- ``POST``
413433
* - ``DELETE``
414-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
434+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
415435
- ``DAG.XCOM``
416436
- ``DELETE``
417437
* - ``GET``
418-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
438+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
419439
- ``DAG.XCOM``
420440
- ``GET``
421441
* - ``PATCH``
422-
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
442+
- ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
423443
- ``DAG.XCOM``
424444
- ``PUT``
425445
* - ``GET``
@@ -551,15 +571,15 @@ source code so it stays up to date as endpoints are added or changed.
551571
- ``Pool``
552572
- ``POST``
553573
* - ``DELETE``
554-
- ``/api/v2/pools/{pool_name:path}``
574+
- ``/api/v2/pools/{pool_name}``
555575
- ``Pool``
556576
- ``DELETE``
557577
* - ``GET``
558-
- ``/api/v2/pools/{pool_name:path}``
578+
- ``/api/v2/pools/{pool_name}``
559579
- ``Pool``
560580
- ``GET``
561581
* - ``PATCH``
562-
- ``/api/v2/pools/{pool_name:path}``
582+
- ``/api/v2/pools/{pool_name}``
563583
- ``Pool``
564584
- ``PUT``
565585
* - ``GET``
@@ -579,15 +599,15 @@ source code so it stays up to date as endpoints are added or changed.
579599
- ``Variable``
580600
- ``POST``
581601
* - ``DELETE``
582-
- ``/api/v2/variables/{variable_key:path}``
602+
- ``/api/v2/variables/{variable_key}``
583603
- ``Variable``
584604
- ``DELETE``
585605
* - ``GET``
586-
- ``/api/v2/variables/{variable_key:path}``
606+
- ``/api/v2/variables/{variable_key}``
587607
- ``Variable``
588608
- ``GET``
589609
* - ``PATCH``
590-
- ``/api/v2/variables/{variable_key:path}``
610+
- ``/api/v2/variables/{variable_key}``
591611
- ``Variable``
592612
- ``PUT``
593613
* - ``GET``

‎providers/fab/docs/auth-manager/access-control.rst‎

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,15 @@ Stable API Permissions
276276
- GET
277277
- Assets.can_read
278278
- Viewer
279-
* - ``/api/v2/assets/{asset_id}/state-store/{key:path}``
279+
* - ``/api/v2/assets/{asset_id}/state-store/{key}``
280280
- DELETE
281281
- Assets.can_delete
282282
- Op
283-
* - ``/api/v2/assets/{asset_id}/state-store/{key:path}``
283+
* - ``/api/v2/assets/{asset_id}/state-store/{key}``
284284
- GET
285285
- Assets.can_read
286286
- Viewer
287-
* - ``/api/v2/assets/{asset_id}/state-store/{key:path}``
287+
* - ``/api/v2/assets/{asset_id}/state-store/{key}``
288288
- PUT
289289
- Assets.can_edit
290290
- Op
@@ -304,7 +304,27 @@ Stable API Permissions
304304
- POST
305305
- DAGs.can_edit, DAG Runs.can_create
306306
- User
307-
* - ``/api/v2/backfills``
307+
* - ``/api/v2/backfills/dry_run``
308+
- POST
309+
- DAGs.can_edit, DAG Runs.can_create
310+
- User
311+
* - ``/api/v2/backfills/{backfill_id}``
312+
- GET
313+
- DAGs.can_read, DAG Runs.can_read
314+
- Viewer
315+
* - ``/api/v2/backfills/{backfill_id}/cancel``
316+
- PUT
317+
- DAGs.can_edit, DAG Runs.can_edit
318+
- User
319+
* - ``/api/v2/backfills/{backfill_id}/dag_runs``
320+
- GET
321+
- DAGs.can_read, DAG Runs.can_read
322+
- Viewer
323+
* - ``/api/v2/backfills/{backfill_id}/pause``
324+
- PUT
325+
- DAGs.can_edit, DAG Runs.can_edit
326+
- User
327+
* - ``/api/v2/backfills/{backfill_id}/unpause``
308328
- PUT
309329
- DAGs.can_edit, DAG Runs.can_edit
310330
- User
@@ -552,19 +572,19 @@ Stable API Permissions
552572
- GET
553573
- DAGs.can_read, DAG Runs.can_read, Task Instances.can_read
554574
- Viewer
555-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
575+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
556576
- DELETE
557577
- DAGs.can_edit, DAG Runs.can_delete, Task Instances.can_delete
558578
- User
559-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
579+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
560580
- GET
561581
- DAGs.can_read, DAG Runs.can_read, Task Instances.can_read
562582
- Viewer
563-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
583+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
564584
- PATCH
565585
- DAGs.can_edit, DAG Runs.can_edit, Task Instances.can_edit
566586
- User
567-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key:path}``
587+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/state-store/{key}``
568588
- PUT
569589
- DAGs.can_edit, DAG Runs.can_edit, Task Instances.can_edit
570590
- User
@@ -584,15 +604,15 @@ Stable API Permissions
584604
- POST
585605
- DAGs.can_edit, XComs.can_create
586606
- Op
587-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
607+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
588608
- DELETE
589609
- DAGs.can_edit, XComs.can_delete
590610
- Op
591-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
611+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
592612
- GET
593613
- DAGs.can_read, XComs.can_read
594614
- Viewer
595-
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key:path}``
615+
* - ``/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/xcomEntries/{xcom_key}``
596616
- PATCH
597617
- DAGs.can_edit, XComs.can_edit
598618
- Op
@@ -724,15 +744,15 @@ Stable API Permissions
724744
- POST
725745
- Pools.can_create
726746
- Op
727-
* - ``/api/v2/pools/{pool_name:path}``
747+
* - ``/api/v2/pools/{pool_name}``
728748
- DELETE
729749
- Pools.can_delete
730750
- Op
731-
* - ``/api/v2/pools/{pool_name:path}``
751+
* - ``/api/v2/pools/{pool_name}``
732752
- GET
733753
- Pools.can_read
734754
- Viewer
735-
* - ``/api/v2/pools/{pool_name:path}``
755+
* - ``/api/v2/pools/{pool_name}``
736756
- PATCH
737757
- Pools.can_edit
738758
- Op
@@ -752,15 +772,15 @@ Stable API Permissions
752772
- POST
753773
- Variables.can_create
754774
- Op
755-
* - ``/api/v2/variables/{variable_key:path}``
775+
* - ``/api/v2/variables/{variable_key}``
756776
- DELETE
757777
- Variables.can_delete
758778
- Op
759-
* - ``/api/v2/variables/{variable_key:path}``
779+
* - ``/api/v2/variables/{variable_key}``
760780
- GET
761781
- Variables.can_read
762782
- Op
763-
* - ``/api/v2/variables/{variable_key:path}``
783+
* - ``/api/v2/variables/{variable_key}``
764784
- PATCH
765785
- Variables.can_edit
766786
- Op

‎scripts/ci/prek/extract_permissions.py‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import ast
3535
import pathlib
36+
import re
3637
import sys
3738
from dataclasses import dataclass
3839

@@ -95,6 +96,34 @@ def _resolve_string_node(node: ast.expr, module_consts: dict[str, str]) -> str:
9596
return f"<unresolved:{ast.unparse(node)}>"
9697

9798

99+
def strip_path_converters(path: str) -> str:
100+
"""
101+
Drop Starlette path converters from a route path.
102+
103+
A route declared as ``/{pool_name:path}`` is served, and documented in the
104+
OpenAPI spec, as ``/{pool_name}``. The converter only tells Starlette how to
105+
match the segment, so leaving it in prints a URL that no client ever calls.
106+
"""
107+
return re.sub(r"\{([^{}:]+):[^{}]+\}", r"{\1}", path)
108+
109+
110+
def _extract_route_path(decorator: ast.Call, module_consts: dict[str, str]) -> str:
111+
"""
112+
Resolve the route path from a route decorator.
113+
114+
FastAPI accepts the path either positionally or as ``path=``, and both forms
115+
are in use here. Reading only the positional form silently resolves the whole
116+
file to its router prefix, which collapses every route onto one bogus row
117+
instead of failing.
118+
"""
119+
if decorator.args:
120+
return _resolve_string_node(decorator.args[0], module_consts)
121+
path_kwarg = next((kw for kw in decorator.keywords if kw.arg == "path"), None)
122+
if path_kwarg is not None:
123+
return _resolve_string_node(path_kwarg.value, module_consts)
124+
raise RouteParseError(f"Route decorator has no path: {ast.unparse(decorator)}")
125+
126+
98127
def _extract_module_string_constants(tree: ast.Module) -> dict[str, str]:
99128
"""
100129
Walk top-level assignments and collect simple string assignments.
@@ -313,17 +342,15 @@ def extract_from_file(path: pathlib.Path) -> list[PermissionEntry]:
313342
continue
314343

315344
# Resolve the route path
316-
route_suffix = ""
317-
if decorator.args:
318-
route_suffix = _resolve_string_node(decorator.args[0], module_consts)
345+
route_suffix = _extract_route_path(decorator, module_consts)
319346

320347
# Resolve the prefix based on the router variable used in the decorator
321348
router_prefix = ""
322349
if isinstance(decorator.func.value, ast.Name):
323350
router_var = decorator.func.value.id
324351
router_prefix = routers.get(router_var, "")
325352

326-
full_path = API_PREFIX + router_prefix + route_suffix
353+
full_path = strip_path_converters(API_PREFIX + router_prefix + route_suffix)
327354

328355
# Extract tag (for grouping in the RST table)
329356
tag = _extract_tag_from_decorator(decorator)

‎scripts/ci/prek/fab_permissions_doc.py‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
PermissionEntry,
4747
RouteParseError,
4848
extract_all_permissions,
49+
strip_path_converters,
4950
)
5051

5152
REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
@@ -303,19 +304,25 @@ def _fab_route_entries() -> list[tuple[str, str, str, str]]:
303304
if route_file.name in {"__init__.py", "router.py"}:
304305
continue
305306
for node in ast.walk(_parse(route_file)):
306-
if not isinstance(node, ast.FunctionDef):
307+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
307308
continue
308309
for deco in node.decorator_list:
309310
if not isinstance(deco, ast.Call):
310311
continue
311312
verb = _name_of(deco.func)
312313
if verb is None or verb.upper() not in _METHOD_TO_ACTION:
313314
continue
314-
if not deco.args or not isinstance(deco.args[0], ast.Constant):
315+
# FastAPI takes the path positionally or as ``path=``; accept both so a
316+
# route written the other way does not quietly drop out of the table.
317+
path_node = deco.args[0] if deco.args else None
318+
if path_node is None:
319+
path_node = next((kw.value for kw in deco.keywords if kw.arg == "path"), None)
320+
if not isinstance(path_node, ast.Constant):
315321
continue
316-
path = deco.args[0].value
322+
path = path_node.value
317323
if not isinstance(path, str):
318324
continue
325+
path = strip_path_converters(path)
319326

320327
perms: list[str] = []
321328
for kw in deco.keywords:

0 commit comments

Comments
 (0)