baha-bouali opened a new pull request, #72230:
URL: https://github.com/apache/airflow/pull/72230
## What
`extract_permissions.py` (added in #67606) already generates the
auth-manager-agnostic
permission reference at `airflow-core/docs/security/api_permissions_ref.rst`.
The FAB provider documents the same endpoints in its own vocabulary —
concrete permission
names such as `Pools.can_read`, plus the minimum built-in role that grants
them — and that
table in `providers/fab/docs/auth-manager/access-control.rst` is still
maintained by hand.
It has drifted, which is what #43430 describes.
This PR generates that table from the same source, and wires it into a prek
hook so it
cannot drift again.
## The drift, measured
The hand-written table has **57 rows**. The API actually exposes **143**.
Comparing them:
- **3 rows state the wrong requirement:**
- `/pools` GET is documented as `Op`, but `VIEWER_PERMISSIONS` contains
`(ACTION_CAN_READ, RESOURCE_POOL)`, so `Viewer` is sufficient.
- `/eventLogs` and `/eventLogs/{event_log_id}` GET omit the base
`DAGs.can_read`
requirement. `requires_access_event_log` delegates to
`requires_access_dag` with
`DagAccessEntity.AUDIT_LOG`, so the base Dag check does run.
- **Stale paths and verbs:** rows still use parameter names and methods that
have since
changed -- `/pools/{pool_name}`, `/roles/{role_name}` (now
`/fab/v1/roles/{name}`), and
`/dags/{dag_id}/clearTaskInstances` documented as `PUT` when the route is
`POST`.
- **Removed endpoints** are still listed, including the old batch endpoints
`/dags/~/dagRuns/list` and `/dags/~/dagRuns/~/taskInstances/list`.
- **Around 90 endpoints are undocumented entirely.**
## How
`scripts/ci/prek/fab_permissions_doc.py` reuses `extract_permissions.py`'s
parser and
renders the FAB view from the same `PermissionEntry` list.
It also parses FAB's own router (`/fab/v1/users`, `/fab/v1/roles`,
`/fab/v1/permissions`).
Those endpoints declare permissions with `requires_fab_custom_view` rather
than the core
`requires_access_*` helpers, so they need separate handling -- but they are
real endpoints
and belong in this table. The router prefix is read from `FAB_AUTH_PREFIX`,
not hardcoded.
Like `extract_permissions.py`, it runs entirely statically — the route
files, FAB's
resource maps and FAB's role definitions are all read with `ast`, so no
Airflow or FAB
import is required and no runtime environment is needed.
It reads FAB's own definitions rather than restating them:
- `_MAP_DAG_ACCESS_ENTITY_TO_FAB_RESOURCE_TYPE` and
`_MAP_ACCESS_VIEW_TO_FAB_RESOURCE_TYPE`
from `fab_auth_manager.py`, for resource expansion.
- `VIEWER_PERMISSIONS` / `USER_PERMISSIONS` / `OP_PERMISSIONS` /
`ADMIN_PERMISSIONS` from
`security_manager/override.py`, for the minimum role. `ROLE_CONFIGS`
stacks these
cumulatively, so the minimum role is the first one whose permissions cover
the
requirement.
- The `RESOURCE_*` display names from `www/security/permissions.py`.
Restating any of those here would create exactly the second source of truth
this is meant
to remove.
## Two details worth flagging for review
- **The action comes from the authorization method, not the HTTP verb.**
They differ:
`/clearTaskInstances` is a `POST` route that authorizes with
`requires_access_dag(method="PUT", ...)`. Using the verb would produce
`can_create`
where the correct answer is `can_edit`.
- **The base Dag check applies to GET as well.** `is_authorized_dag` uses
`"GET" if method == "GET" else "PUT"`, so a sub-entity endpoint requires
`DAGs.can_read` on GET and `DAGs.can_edit` otherwise — it is not skipped
for reads.
## On placement
The generator is placed alongside `extract_permissions.py` in
`scripts/ci/prek/` for
consistency with the other prek scripts, and it reads the FAB definitions
statically rather
than importing them. If you would rather it lived under `providers/fab/` so
ownership sits
with the provider, I am happy to move it — the logic is unchanged either way.
## Changes
- `scripts/ci/prek/fab_permissions_doc.py` — new generator.
- `.pre-commit-config.yaml` — new `generate-fab-permissions-doc` hook,
watching the public
route files, `security.py`, the FAB permission/role definitions, and the
generated table.
- `providers/fab/docs/auth-manager/_api_permissions_table.rst` — generated
output.
- `providers/fab/docs/auth-manager/access-control.rst` — the hand-written
table is replaced
with an `.. include::` of the generated one. The surrounding prose is
unchanged.
## Verification
The check catches a permission change that has not been reflected in the
docs:
```console
$ python scripts/ci/prek/fab_permissions_doc.py --check
[OK] .../providers/fab/docs/auth-manager/_api_permissions_table.rst is up to
date.
$ sed -i '82s/method="GET"/method="POST"/' \
airflow-core/src/airflow/api_fastapi/core_api/routes/public/pools.py
$ git diff --stat
airflow-core/src/airflow/api_fastapi/core_api/routes/public/pools.py
airflow-core/src/airflow/api_fastapi/core_api/routes/public/pools.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ python scripts/ci/prek/fab_permissions_doc.py --check
[FAIL] .../providers/fab/docs/auth-manager/_api_permissions_table.rst is
stale. Run: python fab_permissions_doc.py
$ git checkout
airflow-core/src/airflow/api_fastapi/core_api/routes/public/pools.py
$ python scripts/ci/prek/fab_permissions_doc.py --check
[OK] .../providers/fab/docs/auth-manager/_api_permissions_table.rst is up to
date.
```
Before this PR the same change would have gone undocumented, with nothing to
catch it.
## Regenerating
```
prek run generate-fab-permissions-doc --all-files
```
`--check` mode exits 1 when the file on disk is stale, mirroring
`extract_permissions.py`.
Closes #43430
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]