Skip to content

Commit 16f8170

Browse files
dirraojoaopamaral
authored andcommitted
Deprecated kerberos auth removed (apache#41693)
* deprecatd kerberos auth airflow.api.auth.backend.kerberos_auth and airflow.auth.managers.fab.api.auth.backend.kerberos_aut removed * news fragment added * deprecatd kerberos auth airflow.api.auth.backend.kerberos_auth and airflow.auth.managers.fab.api.auth.backend.kerberos_aut removed
1 parent 6e7ae3c commit 16f8170

File tree

6 files changed

+109
-238
lines changed

6 files changed

+109
-238
lines changed

airflow/api/auth/backend/kerberos_auth.py

-182
This file was deleted.

airflow/auth/managers/fab/api/auth/backend/kerberos_auth.py

-43
This file was deleted.

airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py

+106-8
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,125 @@
1818
from __future__ import annotations
1919

2020
import logging
21-
from functools import partial
22-
from typing import Any, cast
21+
import os
22+
from functools import wraps
23+
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar, cast
2324

25+
import kerberos
26+
from flask import Response, g, make_response, request
2427
from requests_kerberos import HTTPKerberosAuth
2528

26-
from airflow.api.auth.backend.kerberos_auth import (
27-
init_app as base_init_app,
28-
requires_authentication as base_requires_authentication,
29-
)
29+
from airflow.configuration import conf
3030
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
31+
from airflow.utils.net import getfqdn
3132
from airflow.www.extensions.init_auth_manager import get_auth_manager
3233

34+
if TYPE_CHECKING:
35+
from airflow.auth.managers.models.base_user import BaseUser
36+
3337
log = logging.getLogger(__name__)
3438

3539
CLIENT_AUTH: tuple[str, str] | Any | None = HTTPKerberosAuth(service="airflow")
3640

3741

42+
class KerberosService:
43+
"""Class to keep information about the Kerberos Service initialized."""
44+
45+
def __init__(self):
46+
self.service_name = None
47+
48+
49+
class _KerberosAuth(NamedTuple):
50+
return_code: int | None
51+
user: str = ""
52+
token: str | None = None
53+
54+
55+
# Stores currently initialized Kerberos Service
56+
_KERBEROS_SERVICE = KerberosService()
57+
58+
59+
def init_app(app):
60+
"""Initialize application with kerberos."""
61+
hostname = app.config.get("SERVER_NAME")
62+
if not hostname:
63+
hostname = getfqdn()
64+
log.info("Kerberos: hostname %s", hostname)
65+
66+
service = "airflow"
67+
68+
_KERBEROS_SERVICE.service_name = f"{service}@{hostname}"
69+
70+
if "KRB5_KTNAME" not in os.environ:
71+
os.environ["KRB5_KTNAME"] = conf.get("kerberos", "keytab")
72+
73+
try:
74+
log.info("Kerberos init: %s %s", service, hostname)
75+
principal = kerberos.getServerPrincipalDetails(service, hostname)
76+
except kerberos.KrbError as err:
77+
log.warning("Kerberos: %s", err)
78+
else:
79+
log.info("Kerberos API: server is %s", principal)
80+
81+
82+
def _unauthorized():
83+
"""Indicate that authorization is required."""
84+
return Response("Unauthorized", 401, {"WWW-Authenticate": "Negotiate"})
85+
86+
87+
def _forbidden():
88+
return Response("Forbidden", 403)
89+
90+
91+
def _gssapi_authenticate(token) -> _KerberosAuth | None:
92+
state = None
93+
try:
94+
return_code, state = kerberos.authGSSServerInit(_KERBEROS_SERVICE.service_name)
95+
if return_code != kerberos.AUTH_GSS_COMPLETE:
96+
return _KerberosAuth(return_code=None)
97+
98+
if (return_code := kerberos.authGSSServerStep(state, token)) == kerberos.AUTH_GSS_COMPLETE:
99+
return _KerberosAuth(
100+
return_code=return_code,
101+
user=kerberos.authGSSServerUserName(state),
102+
token=kerberos.authGSSServerResponse(state),
103+
)
104+
elif return_code == kerberos.AUTH_GSS_CONTINUE:
105+
return _KerberosAuth(return_code=return_code)
106+
return _KerberosAuth(return_code=return_code)
107+
except kerberos.GSSError:
108+
return _KerberosAuth(return_code=None)
109+
finally:
110+
if state:
111+
kerberos.authGSSServerClean(state)
112+
113+
114+
T = TypeVar("T", bound=Callable)
115+
116+
38117
def find_user(username=None, email=None):
39118
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
40119
return security_manager.find_user(username=username, email=email)
41120

42121

43-
init_app = base_init_app
44-
requires_authentication = partial(base_requires_authentication, find_user=find_user)
122+
def requires_authentication(function: T, find_user: Callable[[str], BaseUser] | None = find_user):
123+
"""Decorate functions that require authentication with Kerberos."""
124+
125+
@wraps(function)
126+
def decorated(*args, **kwargs):
127+
header = request.headers.get("Authorization")
128+
if header:
129+
token = "".join(header.split()[1:])
130+
auth = _gssapi_authenticate(token)
131+
if auth.return_code == kerberos.AUTH_GSS_COMPLETE:
132+
g.user = find_user(auth.user)
133+
response = function(*args, **kwargs)
134+
response = make_response(response)
135+
if auth.token is not None:
136+
response.headers["WWW-Authenticate"] = f"negotiate {auth.token}"
137+
return response
138+
elif auth.return_code != kerberos.AUTH_GSS_CONTINUE:
139+
return _forbidden()
140+
return _unauthorized()
141+
142+
return cast(T, decorated)

docs/apache-airflow-providers-fab/auth-manager/api-authentication.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ To enable Kerberos authentication, set the following in the configuration:
5555
.. code-block:: ini
5656
5757
[api]
58-
auth_backends = airflow.api.auth.backend.kerberos_auth
58+
auth_backends = airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth
5959
6060
[kerberos]
6161
keytab = <KEYTAB>

newsfragments/41693.significant.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removed deprecated auth ``airflow.api.auth.backend.kerberos_auth`` and ``airflow.auth.managers.fab.api.auth.backend.kerberos_auth`` from ``auth_backends``. Please use ``airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth`` instead.

tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616
# under the License.
1717
from __future__ import annotations
1818

19-
from airflow.api.auth.backend.kerberos_auth import (
20-
init_app as base_init_app,
21-
)
2219
from tests.test_utils.compat import ignore_provider_compatibility_error
2320

2421
with ignore_provider_compatibility_error("2.9.0+", __file__):
@@ -27,4 +24,4 @@
2724

2825
class TestKerberosAuth:
2926
def test_init_app(self):
30-
assert init_app == base_init_app
27+
init_app

0 commit comments

Comments
 (0)