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 dd0a8f935b3 Create initial documentation for `KeycloakAuthManager`
(#54246)
dd0a8f935b3 is described below
commit dd0a8f935b3ed26baf749a28fb6420477146def7
Author: Vincent <[email protected]>
AuthorDate: Fri Aug 8 15:57:16 2025 -0400
Create initial documentation for `KeycloakAuthManager` (#54246)
---
.pre-commit-config.yaml | 10 +-
contributing-docs/08_static_code_checks.rst | 2 +
dev/breeze/doc/images/output_static-checks.svg | 12 +-
dev/breeze/doc/images/output_static-checks.txt | 2 +-
dev/breeze/src/airflow_breeze/pre_commit_ids.py | 1 +
devel-common/src/docs/provider_conf.py | 17 +-
providers/keycloak/docs/api-ref/token-api-ref.rst | 22 +++
providers/keycloak/docs/auth-manager/index.rst | 49 ++++++
.../docs/auth-manager/manage/permissions.rst | 22 +++
.../keycloak/docs/auth-manager/manage/users.rst | 23 +++
.../keycloak/docs/auth-manager/setup/config.rst | 60 +++++++
providers/keycloak/docs/auth-manager/setup/env.rst | 33 ++++
providers/keycloak/docs/auth-manager/token.rst | 42 +++++
providers/keycloak/docs/index.rst | 8 +
.../keycloak/auth_manager/openapi/__init__.py | 16 ++
.../v2-keycloak-auth-manager-generated.yaml | 175 +++++++++++++++++++++
.../pre_commit/generate_openapi_spec_keycloak.py | 38 +++++
.../run_generate_openapi_spec_keycloak.py | 45 ++++++
18 files changed, 567 insertions(+), 10 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 80f46592710..09033969c0e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1532,9 +1532,15 @@ repos:
language: python
entry: ./scripts/ci/pre_commit/generate_openapi_spec_fab.py
pass_filenames: false
- files:
^airflow-core/src/airflow/api_fastapi/.*\.py$|^airflow-core/src/airflow/api_fastapi/auth/managers/simple/.*\.py$|^providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/.*\.py$
- exclude: ^airflow-core/src/airflow/api_fastapi/execution_api/.*
+ files:
^providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/.*\.py$
additional_dependencies: ['rich>=12.4.4',
'openapi-spec-validator>=0.7.1']
+ - id: generate-openapi-spec-keycloak
+ name: Generate the FastAPI API spec for Keycloak
+ language: python
+ entry: ./scripts/ci/pre_commit/generate_openapi_spec_keycloak.py
+ pass_filenames: false
+ files:
^providers/keycloak/src/airflow/providers/keycloak/auth_manager/.*\.py$
+ additional_dependencies: [ 'rich>=12.4.4',
'openapi-spec-validator>=0.7.1' ]
- id: check-i18n-json
name: Check i18n files validity
description: Check i18n files are valid json, have no TODOs, and
auto-format them
diff --git a/contributing-docs/08_static_code_checks.rst
b/contributing-docs/08_static_code_checks.rst
index 343dc651806..5b26739a8fc 100644
--- a/contributing-docs/08_static_code_checks.rst
+++ b/contributing-docs/08_static_code_checks.rst
@@ -295,6 +295,8 @@ require Breeze Docker image to be built locally.
+-----------------------------------------------------------+--------------------------------------------------------+---------+
| generate-openapi-spec-fab | Generate the
FastAPI API spec for FAB | * |
+-----------------------------------------------------------+--------------------------------------------------------+---------+
+| generate-openapi-spec-keycloak | Generate the
FastAPI API spec for Keycloak | * |
++-----------------------------------------------------------+--------------------------------------------------------+---------+
| generate-pypi-readme | Generate PyPI
README | |
+-----------------------------------------------------------+--------------------------------------------------------+---------+
| generate-tasksdk-datamodels | Generate
Datamodels for TaskSDK client | * |
diff --git a/dev/breeze/doc/images/output_static-checks.svg
b/dev/breeze/doc/images/output_static-checks.svg
index b78e3631bb5..be5d574d89b 100644
--- a/dev/breeze/doc/images/output_static-checks.svg
+++ b/dev/breeze/doc/images/output_static-checks.svg
@@ -379,12 +379,12 @@
</text><text class="breeze-static-checks-r5" x="0" y="1069.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-43)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1069.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-43)">doctoc | end-of-file-fixer | fix-encoding-pragma | flynt | gci |                 </text><text
class="b [...]
</text><text class="breeze-static-checks-r5" x="0" y="1093.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-44)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1093.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-44)">generate-airflow-diagrams | generate-airflowctl-datamodels |                     </text><text
class="breeze-stat [...]
</text><text class="breeze-static-checks-r5" x="0" y="1118" textLength="12.2"
clip-path="url(#breeze-static-checks-line-45)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1118" textLength="988.2"
clip-path="url(#breeze-static-checks-line-45)">generate-airflowctl-help-images | generate-openapi-spec |                        </text><text
class=" [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">generate-openapi-spec-fab | generate-pypi-readme | generate-tasksdk-datamodels | </text><text
class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12.2"
clip-path="url(#breeze-static-checks [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">generate-volumes-for-sources | go-mockery | go-mod-tidy | gofmt | identity |     </text><text
class="breeze-static-checks-r5" x="1451.8" y="1166.8" textLength="12 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">insert-license | kubeconform | lint-chart-schema | lint-dockerfile |             </text><text
class="breeze-static-checks-r5" x="145 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1215.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-49)">lint-helm-chart | lint-json-schema | lint-markdown | mixed-line-ending |         </text><text
class="breeze-static-checks-r5" x="1451.8" y="1215.6" text [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1240" textLength="988.2"
clip-path="url(#breeze-static-checks-line-50)">mypy-airflow-core | mypy-airflow-ctl | mypy-dev | mypy-devel-common |            </text><text
class="breeze-static-checks-r5" x="1451.8" y="1 [...]
-</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1264.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-51)">mypy-providers | mypy-task-sdk | pretty-format-json |                         
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1142.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-46)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1142.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-46)">generate-openapi-spec-fab | generate-openapi-spec-keycloak | generate-pypi-readme</text><text
class="breeze-static-checks-r5" x="1451.8" y="1142.4" textLength="12.2"
clip-path="url(#breeze-static-checks-line-46)" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1166.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-47)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1166.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-47)">| generate-tasksdk-datamodels | generate-volumes-for-sources | go-mockery |      </text><text
class="breeze-static-checks-r5" x="1451.8" y="1166.8" textLength="12.2" clip-p
[...]
+</text><text class="breeze-static-checks-r5" x="0" y="1191.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-48)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1191.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-48)">go-mod-tidy | gofmt | identity | insert-license | kubeconform | lint-chart-schema</text><text
class="breeze-static-checks-r5" x="1451.8" y="1191.2" textLength="12.2"
clip-path="url(# [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1215.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-49)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1215.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-49)">| lint-dockerfile | lint-helm-chart | lint-json-schema | lint-markdown |         </text><text
class="breeze-static-checks-r5" x="1451.8" y="1215.6" [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1240" textLength="12.2"
clip-path="url(#breeze-static-checks-line-50)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1240" textLength="988.2"
clip-path="url(#breeze-static-checks-line-50)">mixed-line-ending | mypy-airflow-core | mypy-airflow-ctl | mypy-dev |            </text><text
class="breeze-static-checks-r5" x="1451.8" y="1 [...]
+</text><text class="breeze-static-checks-r5" x="0" y="1264.4"
textLength="12.2" clip-path="url(#breeze-static-checks-line-51)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1264.4" textLength="988.2"
clip-path="url(#breeze-static-checks-line-51)">mypy-devel-common | mypy-providers | mypy-task-sdk | pretty-format-json |        </text><text
class="breeze-static-checks-r5" x="1451.8" y="1264.4" textLengt [...]
</text><text class="breeze-static-checks-r5" x="0" y="1288.8"
textLength="12.2" clip-path="url(#breeze-static-checks-line-52)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1288.8" textLength="988.2"
clip-path="url(#breeze-static-checks-line-52)">prevent-deprecated-sqlalchemy-usage | provider-version-compat | pylint |         </text><text
class="breeze-static-checks-r5" x="1451.8" y="1288.8" textLength="12 [...]
</text><text class="breeze-static-checks-r5" x="0" y="1313.2"
textLength="12.2" clip-path="url(#breeze-static-checks-line-53)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1313.2" textLength="988.2"
clip-path="url(#breeze-static-checks-line-53)">python-no-log-warn | replace-bad-characters | rst-backticks | ruff | ruff-format </text><text
class="breeze-static-checks-r5" x="1451.8" y="1313.2" textLength="12.2"
clip-path="url(#breez [...]
</text><text class="breeze-static-checks-r5" x="0" y="1337.6"
textLength="12.2" clip-path="url(#breeze-static-checks-line-54)">│</text><text
class="breeze-static-checks-r7" x="451.4" y="1337.6" textLength="988.2"
clip-path="url(#breeze-static-checks-line-54)">| shellcheck | trailing-whitespace | ts-compile-lint-simple-auth-manager-ui |    </text><text
class="breeze-static-checks-r5" x="1451.8" y="1337.6" textLength="12.2"
clip-path="url(# [...]
diff --git a/dev/breeze/doc/images/output_static-checks.txt
b/dev/breeze/doc/images/output_static-checks.txt
index 5d370fd0d00..9db41863d21 100644
--- a/dev/breeze/doc/images/output_static-checks.txt
+++ b/dev/breeze/doc/images/output_static-checks.txt
@@ -1 +1 @@
-1ff02ab3c1c4049df0ca8b48c6e0436e
+5eaaf5cd76e63aae4a774847c869e11f
diff --git a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
index bfa9aa2c19c..2dbdf86ea86 100644
--- a/dev/breeze/src/airflow_breeze/pre_commit_ids.py
+++ b/dev/breeze/src/airflow_breeze/pre_commit_ids.py
@@ -111,6 +111,7 @@ PRE_COMMIT_LIST = [
"generate-airflowctl-help-images",
"generate-openapi-spec",
"generate-openapi-spec-fab",
+ "generate-openapi-spec-keycloak",
"generate-pypi-readme",
"generate-tasksdk-datamodels",
"generate-volumes-for-sources",
diff --git a/devel-common/src/docs/provider_conf.py
b/devel-common/src/docs/provider_conf.py
index e2794f00bb2..3c65f2d0d2e 100644
--- a/devel-common/src/docs/provider_conf.py
+++ b/devel-common/src/docs/provider_conf.py
@@ -119,7 +119,7 @@ smartquotes_excludes = SMARTQUOTES_EXCLUDES
# ones.
extensions = BASIC_SPHINX_EXTENSIONS
-PROVIDER_PACKAGES_WITH_REDOC = ["apache-airflow-providers-fab"]
+PROVIDER_PACKAGES_WITH_REDOC = ["apache-airflow-providers-fab",
"apache-airflow-providers-keycloak"]
if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
extensions.extend(
@@ -356,6 +356,9 @@ if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
__file__ as fab_auth_manager_fastapi_api_file,
)
from airflow.providers.fab.auth_manager.openapi import __file__ as
fab_auth_manager_flask_api_file
+ from airflow.providers.keycloak.auth_manager.openapi import (
+ __file__ as keycloak_auth_manager_fastapi_api_file,
+ )
fab_auth_manager_flask_api_path =
Path(fab_auth_manager_flask_api_file).parent.joinpath(
"v1-flask-api.yaml"
@@ -363,6 +366,9 @@ if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
fab_auth_manager_fastapi_api_path =
Path(fab_auth_manager_fastapi_api_file).parent.joinpath(
"v2-fab-auth-manager-generated.yaml"
)
+ keycloak_auth_manager_fastapi_api_path =
Path(keycloak_auth_manager_fastapi_api_file).parent.joinpath(
+ "v2-keycloak-auth-manager-generated.yaml"
+ )
redoc = [
{
"name": "Fab auth manager API",
@@ -381,4 +387,13 @@ if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
"no-auto-auth": True,
},
},
+ {
+ "name": "Keycloak auth manager token API",
+ "page": "api-ref/token-api-ref",
+ "spec": keycloak_auth_manager_fastapi_api_path.as_posix(),
+ "opts": {
+ "hide-hostname": True,
+ "no-auto-auth": True,
+ },
+ },
]
diff --git a/providers/keycloak/docs/api-ref/token-api-ref.rst
b/providers/keycloak/docs/api-ref/token-api-ref.rst
new file mode 100644
index 00000000000..e9003e082d0
--- /dev/null
+++ b/providers/keycloak/docs/api-ref/token-api-ref.rst
@@ -0,0 +1,22 @@
+
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Keycloak auth manager token API
+===============================
+
+It's a stub file. It will be automatically converted during the build process
into valid documentation by the Sphinx plugin. See: /docs/conf.py
diff --git a/providers/keycloak/docs/auth-manager/index.rst
b/providers/keycloak/docs/auth-manager/index.rst
new file mode 100644
index 00000000000..9dc2a231d97
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/index.rst
@@ -0,0 +1,49 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+=====================
+Keycloak auth manager
+=====================
+
+.. warning::
+ The Keycloak auth manager is alpha/experimental at the moment and may be
subject to change without warning.
+
+The Keycloak auth manager is an authentication and authorization manager that
uses Keycloak as the identity provider.
+It enables you to manage users, roles, groups, and permissions entirely within
Keycloak.
+
+**Getting started**
+
+.. toctree::
+ :maxdepth: 2
+
+ setup/env
+ setup/config
+
+**Manage the environment**
+
+.. toctree::
+ :maxdepth: 2
+
+ manage/users
+ manage/permissions
+
+**Generate a JWT token to use Airflow public API**
+
+.. toctree::
+ :maxdepth: 2
+
+ token
diff --git a/providers/keycloak/docs/auth-manager/manage/permissions.rst
b/providers/keycloak/docs/auth-manager/manage/permissions.rst
new file mode 100644
index 00000000000..644c21e963c
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/manage/permissions.rst
@@ -0,0 +1,22 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+==================================================
+Manage user permissions with Keycloak auth manager
+==================================================
+
+To be completed
diff --git a/providers/keycloak/docs/auth-manager/manage/users.rst
b/providers/keycloak/docs/auth-manager/manage/users.rst
new file mode 100644
index 00000000000..b1cb4028536
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/manage/users.rst
@@ -0,0 +1,23 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+=======================================
+Manage users with Keycloak auth manager
+=======================================
+
+All users having access to the Airflow environment must be defined in Keycloak.
+To understand better how to manage users and groups in Keycloak, best is to
refer to `official Keycloak documentation
<https://www.keycloak.org/docs/latest/server_admin/index.html#assembly-managing-users_server_administration_guide>`__.
diff --git a/providers/keycloak/docs/auth-manager/setup/config.rst
b/providers/keycloak/docs/auth-manager/setup/config.rst
new file mode 100644
index 00000000000..8a89194d062
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/setup/config.rst
@@ -0,0 +1,60 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+==================================================
+Configure Airflow to use the Keycloak auth manager
+==================================================
+
+To use the Keycloak auth manager in your Airflow environment, you must
configure it in your Airflow configuration.
+
+.. note::
+ These configuration options must be set on the hosts/environments running
the Airflow API server.
+
+.. code-block:: ini
+
+ [core]
+ auth_manager =
airflow.providers.keycloak.auth_manager.keycloak_auth_manager.KeycloakAuthManager
+
+or
+
+.. code-block:: bash
+
+ export
AIRFLOW__CORE__AUTH_MANAGER='airflow.providers.keycloak.auth_manager.keycloak_auth_manager.KeycloakAuthManager'
+
+Additionally, you'll need to provide some Keycloak-specific configuration
options.
+
+Config Options
+--------------
+
+There are a number of configuration options available, which can either be set
directly in the airflow.cfg file under
+an ``keycloak_auth_manager`` section or via environment variables using the
``AIRFLOW__KEYCLOAK_AUTH_MANAGER__<OPTION_NAME>`` format,
+for example ``AIRFLOW__KEYCLOAK_AUTH_MANAGER__REALM = "Airflow"``. For
+more information on how to set these options, see
`<https://airflow.apache.org/docs/apache-airflow/stable/howto/set-config.html>`__.
+
+Required config options:
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``client_id``. Client ID configured in Keycloak to integrate with Airflow.
This client must follow the standard ``OpenID Connect authentication flow``.
+- ``client_secret``. Secret associated to the client configured in Keycloak to
integrate with Airflow.
+- ``realm``. Realm configured in Keycloak associated to Airflow. This realm
define all users, roles, groups and permissions used by Airflow.
+
+Optional config options:
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``server_url``. Keycloak server URL. This server URL is used by the Airflow
API server to communicate with Keycloak.
+ If the Airflow API server and Keycloak are running in Docker, set
"http://host.docker.internal:<PORT>" (default value).
+ You do not need to set this configuration option if you are running Keycloak
with Breeze.
diff --git a/providers/keycloak/docs/auth-manager/setup/env.rst
b/providers/keycloak/docs/auth-manager/setup/env.rst
new file mode 100644
index 00000000000..cc7a1b012dd
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/setup/env.rst
@@ -0,0 +1,33 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+============================
+Setup a Keycloak environment
+============================
+
+To use the Keycloak auth manager, you must have a running Keycloak environment.
+This is where you define users, groups, roles, and permissions.
+Airflow delegates all authentication and authorization operations to Keycloak,
querying it for every access decision.
+
+If you don't have a Keycloak environment set up, please refer to the `official
Keycloak documentation <https://www.keycloak.org/guides>`__ for installation
and configuration guidance.
+
+Setup and run Keycloak with Breeze (local development)
+------------------------------------------------------
+
+Setting up and running a Keycloak environment with Breeze for local
development is straightforward.
+Simply run ``breeze start-airflow --integration keycloak``. Breeze will handle
starting Keycloak in a separate container.
+The Keycloak admin console will be available at http://localhost:48080/.
diff --git a/providers/keycloak/docs/auth-manager/token.rst
b/providers/keycloak/docs/auth-manager/token.rst
new file mode 100644
index 00000000000..b38904b3516
--- /dev/null
+++ b/providers/keycloak/docs/auth-manager/token.rst
@@ -0,0 +1,42 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Generate JWT token with Keycloak auth manager
+=============================================
+
+.. note::
+ This guide only applies if your environment is configured with Keycloak
auth manager.
+
+In order to use the :doc:`Airflow public API
<apache-airflow:stable-rest-api-ref>`, you need a JWT token for authentication.
+You can then include this token in your Airflow public API requests.
+To generate a JWT token, use the ``Create Token`` API in
:doc:`/api-ref/token-api-ref`.
+
+Example
+'''''''
+
+.. code-block:: bash
+
+ ENDPOINT_URL="http://localhost:8080"
+ curl -X 'POST' \
+ "${ENDPOINT_URL}/auth/token" \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "username": "<username>",
+ "password": "<password>"
+ }'
+
+This process will return a token that you can use in the Airflow public API
requests.
diff --git a/providers/keycloak/docs/index.rst
b/providers/keycloak/docs/index.rst
index 7e235841eda..2a98598bf8a 100644
--- a/providers/keycloak/docs/index.rst
+++ b/providers/keycloak/docs/index.rst
@@ -29,6 +29,13 @@
Changelog <changelog>
Security <security>
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+ :caption: Guides
+
+ Keycloak auth manager <auth-manager/index>
+
.. toctree::
:hidden:
:maxdepth: 1
@@ -36,6 +43,7 @@
Python API <_api/airflow/providers/keycloak/index>
Configuration <configurations-ref>
+ Keycloak auth manager token API <api-ref/token-api-ref>
.. toctree::
:hidden:
diff --git
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/__init__.py
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/v2-keycloak-auth-manager-generated.yaml
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/v2-keycloak-auth-manager-generated.yaml
new file mode 100644
index 00000000000..0932c8a95ea
--- /dev/null
+++
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/openapi/v2-keycloak-auth-manager-generated.yaml
@@ -0,0 +1,175 @@
+openapi: 3.1.0
+info:
+ title: Keycloak auth manager sub application
+ description: This is the Keycloak auth manager fastapi sub application. This
API
+ is only available if the auth manager used in the Airflow environment is
Keycloak
+ auth manager. This sub application provides login routes.
+ version: 0.1.0
+paths:
+ /auth/login:
+ get:
+ tags:
+ - KeycloakAuthManagerLogin
+ summary: Login
+ description: Initiate the authentication.
+ operationId: login
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ /auth/login_callback:
+ get:
+ tags:
+ - KeycloakAuthManagerLogin
+ summary: Login Callback
+ description: Authenticate the user.
+ operationId: login_callback
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ /auth/refresh:
+ get:
+ tags:
+ - KeycloakAuthManagerLogin
+ summary: Refresh
+ description: Refresh the token.
+ operationId: refresh
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ security:
+ - OAuth2PasswordBearer: []
+ - HTTPBearer: []
+ /auth/token:
+ post:
+ tags:
+ - KeycloakAuthManagerToken
+ summary: Create Token
+ operationId: create_token
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TokenBody'
+ required: true
+ responses:
+ '201':
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TokenResponse'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/HTTPExceptionResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/HTTPExceptionResponse'
+ '422':
+ description: Validation Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/HTTPValidationError'
+components:
+ schemas:
+ HTTPExceptionResponse:
+ properties:
+ detail:
+ anyOf:
+ - type: string
+ - additionalProperties: true
+ type: object
+ title: Detail
+ type: object
+ required:
+ - detail
+ title: HTTPExceptionResponse
+ description: HTTPException Model used for error response.
+ HTTPValidationError:
+ properties:
+ detail:
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ type: array
+ title: Detail
+ type: object
+ title: HTTPValidationError
+ TokenBody:
+ properties:
+ username:
+ type: string
+ title: Username
+ password:
+ type: string
+ title: Password
+ additionalProperties: false
+ type: object
+ required:
+ - username
+ - password
+ title: TokenBody
+ description: Token serializer for post bodies.
+ TokenResponse:
+ properties:
+ access_token:
+ type: string
+ title: Access Token
+ type: object
+ required:
+ - access_token
+ title: TokenResponse
+ description: Token serializer for responses.
+ ValidationError:
+ properties:
+ loc:
+ items:
+ anyOf:
+ - type: string
+ - type: integer
+ type: array
+ title: Location
+ msg:
+ type: string
+ title: Message
+ type:
+ type: string
+ title: Error Type
+ type: object
+ required:
+ - loc
+ - msg
+ - type
+ title: ValidationError
+ securitySchemes:
+ OAuth2PasswordBearer:
+ type: oauth2
+ description: To authenticate Airflow API requests, clients must include
a JWT
+ (JSON Web Token) in the Authorization header of each request. This
token is
+ used to verify the identity of the client and ensure that they have
the appropriate
+ permissions to access the requested resources. You can use the
endpoint ``POST
+ /auth/token`` in order to generate a JWT token. Upon successful
authentication,
+ the server will issue a JWT token that contains the necessary
information
+ (such as user identity and scope) to authenticate subsequent requests.
To
+ learn more about Airflow public API authentication, please read
https://airflow.apache.org/docs/apache-airflow/stable/security/api.html.
+ flows:
+ password:
+ scopes: {}
+ tokenUrl: /auth/token
+ HTTPBearer:
+ type: http
+ scheme: bearer
diff --git a/scripts/ci/pre_commit/generate_openapi_spec_keycloak.py
b/scripts/ci/pre_commit/generate_openapi_spec_keycloak.py
new file mode 100755
index 00000000000..35632cf7fb8
--- /dev/null
+++ b/scripts/ci/pre_commit/generate_openapi_spec_keycloak.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).parent.resolve()))
+from common_precommit_utils import (
+ initialize_breeze_precommit,
+ run_command_via_breeze_shell,
+ validate_cmd_result,
+)
+
+initialize_breeze_precommit(__name__, __file__)
+
+cmd_result = run_command_via_breeze_shell(
+ ["python3",
"/opt/airflow/scripts/in_container/run_generate_openapi_spec_keycloak.py"],
+ backend="postgres",
+ skip_environment_initialization=False,
+)
+
+validate_cmd_result(cmd_result)
diff --git a/scripts/in_container/run_generate_openapi_spec_keycloak.py
b/scripts/in_container/run_generate_openapi_spec_keycloak.py
new file mode 100755
index 00000000000..648bfa1b179
--- /dev/null
+++ b/scripts/in_container/run_generate_openapi_spec_keycloak.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import sys
+from pathlib import Path
+
+from airflow.providers.keycloak.auth_manager import __file__ as
KEYCLOAK_AUTH_MANAGER_PATH
+from airflow.providers.keycloak.auth_manager.keycloak_auth_manager import
KeycloakAuthManager
+from airflow.providers_manager import ProvidersManager
+
+sys.path.insert(0, str(Path(__file__).parent.resolve()))
+from in_container_utils import console, generate_openapi_file,
validate_openapi_file
+
+KEYCLOAK_AUTH_MANAGER_OPENAPI_SPEC_FILE = (
+ Path(KEYCLOAK_AUTH_MANAGER_PATH).parent / "openapi" /
"v2-keycloak-auth-manager-generated.yaml"
+)
+
+ProvidersManager().initialize_providers_configuration()
+
+# Generate Keycloak auth manager openapi spec
+keycloak_auth_manager_app = KeycloakAuthManager().get_fastapi_app()
+if keycloak_auth_manager_app:
+ generate_openapi_file(
+ app=keycloak_auth_manager_app,
file_path=KEYCLOAK_AUTH_MANAGER_OPENAPI_SPEC_FILE, prefix="/auth"
+ )
+ validate_openapi_file(KEYCLOAK_AUTH_MANAGER_OPENAPI_SPEC_FILE)
+else:
+ console.print("[red]Keycloak auth manager app not found. Skipping OpenAPI
spec generation.[/]")
+ sys.exit(1)