apache-airflow-providers-fab 1.5.3__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airflow/providers/fab/LICENSE +0 -52
- airflow/providers/fab/__init__.py +3 -3
- airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +4 -5
- airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py +5 -5
- airflow/providers/fab/auth_manager/api/auth/backend/session.py +2 -2
- airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +15 -15
- airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +13 -14
- airflow/providers/fab/auth_manager/api_fastapi/__init__.py +16 -0
- airflow/providers/fab/auth_manager/api_fastapi/datamodels/__init__.py +16 -0
- airflow/providers/fab/auth_manager/api_fastapi/datamodels/login.py +32 -0
- airflow/providers/fab/auth_manager/api_fastapi/openapi/__init__.py +16 -0
- airflow/providers/fab/auth_manager/api_fastapi/openapi/v1-generated.yaml +153 -0
- airflow/providers/fab/auth_manager/api_fastapi/routes/__init__.py +16 -0
- airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +51 -0
- airflow/providers/fab/auth_manager/api_fastapi/services/__init__.py +16 -0
- airflow/providers/fab/auth_manager/api_fastapi/services/login.py +58 -0
- airflow/providers/fab/auth_manager/cli_commands/db_command.py +1 -3
- airflow/providers/fab/auth_manager/cli_commands/user_command.py +2 -2
- airflow/providers/fab/auth_manager/cli_commands/utils.py +12 -11
- airflow/providers/fab/auth_manager/fab_auth_manager.py +238 -126
- airflow/providers/fab/auth_manager/models/__init__.py +1 -1
- airflow/providers/fab/auth_manager/models/anonymous_user.py +1 -1
- airflow/providers/fab/auth_manager/models/db.py +22 -5
- airflow/providers/fab/auth_manager/openapi/v1.yaml +9 -0
- airflow/providers/fab/auth_manager/schemas/user_schema.py +1 -1
- airflow/providers/fab/auth_manager/security_manager/override.py +186 -655
- airflow/providers/fab/auth_manager/views/permissions.py +1 -1
- airflow/providers/fab/auth_manager/views/roles_list.py +1 -1
- airflow/providers/fab/auth_manager/views/user.py +1 -1
- airflow/providers/fab/auth_manager/views/user_edit.py +1 -1
- airflow/providers/fab/auth_manager/views/user_stats.py +1 -1
- airflow/providers/fab/get_provider_info.py +29 -34
- airflow/providers/fab/www/airflow_flask_app.py +31 -0
- airflow/providers/fab/www/api_connexion/__init__.py +17 -0
- airflow/providers/fab/www/api_connexion/exceptions.py +197 -0
- airflow/providers/fab/www/api_connexion/parameters.py +131 -0
- airflow/providers/fab/www/api_connexion/security.py +84 -0
- airflow/providers/fab/www/api_connexion/types.py +30 -0
- airflow/providers/fab/www/app.py +120 -0
- airflow/providers/fab/www/auth.py +350 -0
- airflow/providers/fab/www/constants.py +28 -0
- airflow/providers/fab/www/extensions/__init__.py +16 -0
- airflow/providers/fab/www/extensions/init_appbuilder.py +606 -0
- airflow/providers/fab/www/extensions/init_jinja_globals.py +82 -0
- airflow/providers/fab/www/extensions/init_manifest_files.py +61 -0
- airflow/providers/fab/www/extensions/init_security.py +61 -0
- airflow/providers/fab/www/extensions/init_session.py +64 -0
- airflow/providers/fab/www/extensions/init_views.py +177 -0
- airflow/providers/fab/www/package-lock.json +8939 -0
- airflow/providers/fab/www/package.json +77 -0
- airflow/providers/fab/www/security/__init__.py +17 -0
- airflow/providers/fab/www/security/permissions.py +126 -0
- airflow/providers/fab/www/security_appless.py +44 -0
- airflow/providers/fab/www/security_manager.py +122 -0
- airflow/providers/fab/www/session.py +41 -0
- airflow/providers/fab/www/static/css/bootstrap-theme.css +6215 -0
- airflow/providers/fab/www/static/css/flash.css +57 -0
- airflow/providers/fab/www/static/css/loading-dots.css +60 -0
- airflow/providers/fab/www/static/css/main.css +676 -0
- airflow/providers/fab/www/static/css/material-icons.css +84 -0
- airflow/providers/fab/www/static/dist/48f0ea180c40270a5b05.png +1 -0
- airflow/providers/fab/www/static/dist/649c0b07771e68fafdeb.png +1 -0
- airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.css +33 -0
- airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.js +1 -0
- airflow/providers/fab/www/static/dist/f7490d556a6c42e49ba4.png +1 -0
- airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.css +18 -0
- airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.js +1 -0
- airflow/providers/fab/www/static/dist/jquery-ui.min.css +5 -0
- airflow/providers/fab/www/static/dist/jquery-ui.min.js +2 -0
- airflow/providers/fab/www/static/dist/jquery-ui.min.js.LICENSE.txt +4 -0
- airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.css +18 -0
- airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.js +1 -0
- airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.css +18 -0
- airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.js +2 -0
- airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.js.LICENSE.txt +18 -0
- airflow/providers/fab/www/static/dist/manifest.json +20 -0
- airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.css +18 -0
- airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.js +1 -0
- airflow/providers/fab/www/static/dist/moment.624b1f00ba723d39ce06.js +2 -0
- airflow/providers/fab/www/static/dist/moment.624b1f00ba723d39ce06.js.LICENSE.txt +11 -0
- airflow/providers/fab/www/static/dist/oss-licenses.json +20 -0
- airflow/providers/fab/www/static/js/datetime_utils.js +134 -0
- airflow/providers/fab/www/static/js/main.js +324 -0
- airflow/providers/fab/www/static/sort_asc.png +0 -0
- airflow/providers/fab/www/static/sort_both.png +0 -0
- airflow/providers/fab/www/static/sort_desc.png +0 -0
- airflow/providers/fab/www/templates/airflow/_messages.html +30 -0
- airflow/providers/fab/www/templates/airflow/error.html +35 -0
- airflow/providers/fab/www/templates/airflow/main.html +78 -0
- airflow/providers/fab/www/templates/airflow/traceback.html +53 -0
- airflow/providers/fab/www/templates/appbuilder/flash.html +34 -0
- airflow/providers/fab/www/templates/appbuilder/index.html +20 -0
- airflow/providers/fab/www/templates/appbuilder/navbar.html +60 -0
- airflow/providers/fab/www/templates/appbuilder/navbar_menu.html +60 -0
- airflow/providers/fab/www/templates/appbuilder/navbar_right.html +64 -0
- airflow/providers/fab/www/utils.py +288 -0
- airflow/providers/fab/www/views.py +129 -0
- airflow/providers/fab/www/webpack.config.js +213 -0
- {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/METADATA +29 -37
- apache_airflow_providers_fab-2.0.0.dist-info/RECORD +125 -0
- {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/WHEEL +1 -1
- airflow/providers/fab/auth_manager/decorators/auth.py +0 -126
- apache_airflow_providers_fab-1.5.3.dist-info/RECORD +0 -51
- /airflow/providers/fab/{auth_manager/decorators → www}/__init__.py +0 -0
- {apache_airflow_providers_fab-1.5.3.dist-info → apache_airflow_providers_fab-2.0.0.dist-info}/entry_points.txt +0 -0
airflow/providers/fab/LICENSE
CHANGED
@@ -199,55 +199,3 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
199
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200
200
|
See the License for the specific language governing permissions and
|
201
201
|
limitations under the License.
|
202
|
-
|
203
|
-
============================================================================
|
204
|
-
APACHE AIRFLOW SUBCOMPONENTS:
|
205
|
-
|
206
|
-
The Apache Airflow project contains subcomponents with separate copyright
|
207
|
-
notices and license terms. Your use of the source code for the these
|
208
|
-
subcomponents is subject to the terms and conditions of the following
|
209
|
-
licenses.
|
210
|
-
|
211
|
-
|
212
|
-
========================================================================
|
213
|
-
Third party Apache 2.0 licenses
|
214
|
-
========================================================================
|
215
|
-
|
216
|
-
The following components are provided under the Apache 2.0 License.
|
217
|
-
See project link for details. The text of each license is also included
|
218
|
-
at 3rd-party-licenses/LICENSE-[project].txt.
|
219
|
-
|
220
|
-
(ALv2 License) hue v4.3.0 (https://github.com/cloudera/hue/)
|
221
|
-
(ALv2 License) jqclock v2.3.0 (https://github.com/JohnRDOrazio/jQuery-Clock-Plugin)
|
222
|
-
(ALv2 License) bootstrap3-typeahead v4.0.2 (https://github.com/bassjobsen/Bootstrap-3-Typeahead)
|
223
|
-
(ALv2 License) connexion v2.7.0 (https://github.com/zalando/connexion)
|
224
|
-
|
225
|
-
========================================================================
|
226
|
-
MIT licenses
|
227
|
-
========================================================================
|
228
|
-
|
229
|
-
The following components are provided under the MIT License. See project link for details.
|
230
|
-
The text of each license is also included at 3rd-party-licenses/LICENSE-[project].txt.
|
231
|
-
|
232
|
-
(MIT License) jquery v3.5.1 (https://jquery.org/license/)
|
233
|
-
(MIT License) dagre-d3 v0.6.4 (https://github.com/cpettitt/dagre-d3)
|
234
|
-
(MIT License) bootstrap v3.4.1 (https://github.com/twbs/bootstrap/)
|
235
|
-
(MIT License) d3-tip v0.9.1 (https://github.com/Caged/d3-tip)
|
236
|
-
(MIT License) dataTables v1.10.25 (https://datatables.net)
|
237
|
-
(MIT License) normalize.css v3.0.2 (http://necolas.github.io/normalize.css/)
|
238
|
-
(MIT License) ElasticMock v1.3.2 (https://github.com/vrcmarcos/elasticmock)
|
239
|
-
(MIT License) MomentJS v2.24.0 (http://momentjs.com/)
|
240
|
-
(MIT License) eonasdan-bootstrap-datetimepicker v4.17.49 (https://github.com/eonasdan/bootstrap-datetimepicker/)
|
241
|
-
|
242
|
-
========================================================================
|
243
|
-
BSD 3-Clause licenses
|
244
|
-
========================================================================
|
245
|
-
The following components are provided under the BSD 3-Clause license. See project links for details.
|
246
|
-
The text of each license is also included at 3rd-party-licenses/LICENSE-[project].txt.
|
247
|
-
|
248
|
-
(BSD 3 License) d3 v5.16.0 (https://d3js.org)
|
249
|
-
(BSD 3 License) d3-shape v2.1.0 (https://github.com/d3/d3-shape)
|
250
|
-
(BSD 3 License) cgroupspy 0.2.1 (https://github.com/cloudsigma/cgroupspy)
|
251
|
-
|
252
|
-
========================================================================
|
253
|
-
See 3rd-party-licenses/LICENSES-ui.txt for packages used in `/airflow/www`
|
@@ -29,11 +29,11 @@ from airflow import __version__ as airflow_version
|
|
29
29
|
|
30
30
|
__all__ = ["__version__"]
|
31
31
|
|
32
|
-
__version__ = "
|
32
|
+
__version__ = "2.0.0"
|
33
33
|
|
34
34
|
if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
|
35
|
-
"
|
35
|
+
"3.0.0"
|
36
36
|
):
|
37
37
|
raise RuntimeError(
|
38
|
-
f"The package `apache-airflow-providers-fab:{__version__}` needs Apache Airflow
|
38
|
+
f"The package `apache-airflow-providers-fab:{__version__}` needs Apache Airflow 3.0.0+"
|
39
39
|
)
|
@@ -25,10 +25,10 @@ from flask import Response, current_app, request
|
|
25
25
|
from flask_appbuilder.const import AUTH_LDAP
|
26
26
|
from flask_login import login_user
|
27
27
|
|
28
|
-
from airflow.
|
29
|
-
from airflow.www.extensions.init_auth_manager import get_auth_manager
|
28
|
+
from airflow.api_fastapi.app import get_auth_manager
|
30
29
|
|
31
30
|
if TYPE_CHECKING:
|
31
|
+
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
|
32
32
|
from airflow.providers.fab.auth_manager.models import User
|
33
33
|
|
34
34
|
CLIENT_AUTH: tuple[str, str] | Any | None = None
|
@@ -45,8 +45,7 @@ def auth_current_user() -> User | None:
|
|
45
45
|
auth = request.authorization
|
46
46
|
if auth is None or not auth.username or not auth.password:
|
47
47
|
return None
|
48
|
-
|
49
|
-
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
48
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
50
49
|
user = None
|
51
50
|
if security_manager.auth_type == AUTH_LDAP:
|
52
51
|
user = security_manager.auth_user_ldap(auth.username, auth.password)
|
@@ -67,4 +66,4 @@ def requires_authentication(function: T):
|
|
67
66
|
else:
|
68
67
|
return Response("Unauthorized", 401, {"WWW-Authenticate": "Basic"})
|
69
68
|
|
70
|
-
return cast(T, decorated)
|
69
|
+
return cast("T", decorated)
|
@@ -26,13 +26,13 @@ import kerberos
|
|
26
26
|
from flask import Response, current_app, g, make_response, request
|
27
27
|
from requests_kerberos import HTTPKerberosAuth
|
28
28
|
|
29
|
+
from airflow.api_fastapi.app import get_auth_manager
|
29
30
|
from airflow.configuration import conf
|
30
|
-
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
|
31
31
|
from airflow.utils.net import getfqdn
|
32
|
-
from airflow.www.extensions.init_auth_manager import get_auth_manager
|
33
32
|
|
34
33
|
if TYPE_CHECKING:
|
35
|
-
from airflow.auth.managers.models.base_user import BaseUser
|
34
|
+
from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
|
35
|
+
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
|
36
36
|
|
37
37
|
log = logging.getLogger(__name__)
|
38
38
|
|
@@ -115,7 +115,7 @@ T = TypeVar("T", bound=Callable)
|
|
115
115
|
|
116
116
|
|
117
117
|
def find_user(username=None, email=None):
|
118
|
-
security_manager = cast(
|
118
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
119
119
|
return security_manager.find_user(username=username, email=email)
|
120
120
|
|
121
121
|
|
@@ -143,4 +143,4 @@ def requires_authentication(function: T, find_user: Callable[[str], BaseUser] |
|
|
143
143
|
return _forbidden()
|
144
144
|
return _unauthorized()
|
145
145
|
|
146
|
-
return cast(T, decorated)
|
146
|
+
return cast("T", decorated)
|
@@ -23,7 +23,7 @@ from typing import Any, Callable, TypeVar, cast
|
|
23
23
|
|
24
24
|
from flask import Response
|
25
25
|
|
26
|
-
from airflow.
|
26
|
+
from airflow.api_fastapi.app import get_auth_manager
|
27
27
|
|
28
28
|
CLIENT_AUTH: tuple[str, str] | Any | None = None
|
29
29
|
|
@@ -44,4 +44,4 @@ def requires_authentication(function: T):
|
|
44
44
|
return Response("Unauthorized", 401, {})
|
45
45
|
return function(*args, **kwargs)
|
46
46
|
|
47
|
-
return cast(T, decorated)
|
47
|
+
return cast("T", decorated)
|
@@ -24,9 +24,7 @@ from flask import request
|
|
24
24
|
from marshmallow import ValidationError
|
25
25
|
from sqlalchemy import asc, desc, func, select
|
26
26
|
|
27
|
-
from airflow.
|
28
|
-
from airflow.api_connexion.parameters import check_limit, format_parameters
|
29
|
-
from airflow.api_connexion.security import requires_access_custom_view
|
27
|
+
from airflow.api_fastapi.app import get_auth_manager
|
30
28
|
from airflow.providers.fab.auth_manager.models import Action, Role
|
31
29
|
from airflow.providers.fab.auth_manager.schemas.role_and_permission_schema import (
|
32
30
|
ActionCollection,
|
@@ -35,12 +33,15 @@ from airflow.providers.fab.auth_manager.schemas.role_and_permission_schema impor
|
|
35
33
|
role_collection_schema,
|
36
34
|
role_schema,
|
37
35
|
)
|
38
|
-
from airflow.providers.fab.
|
39
|
-
from airflow.
|
40
|
-
from airflow.www.
|
36
|
+
from airflow.providers.fab.www.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound
|
37
|
+
from airflow.providers.fab.www.api_connexion.parameters import check_limit, format_parameters
|
38
|
+
from airflow.providers.fab.www.api_connexion.security import requires_access_custom_view
|
39
|
+
from airflow.providers.fab.www.security import permissions
|
41
40
|
|
42
41
|
if TYPE_CHECKING:
|
43
|
-
from airflow.
|
42
|
+
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
|
43
|
+
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
|
44
|
+
from airflow.providers.fab.www.api_connexion.types import APIResponse, UpdateMask
|
44
45
|
|
45
46
|
|
46
47
|
def _check_action_and_resource(sm: FabAirflowSecurityManagerOverride, perms: list[tuple[str, str]]) -> None:
|
@@ -59,7 +60,7 @@ def _check_action_and_resource(sm: FabAirflowSecurityManagerOverride, perms: lis
|
|
59
60
|
@requires_access_custom_view("GET", permissions.RESOURCE_ROLE)
|
60
61
|
def get_role(*, role_name: str) -> APIResponse:
|
61
62
|
"""Get role."""
|
62
|
-
security_manager = cast(
|
63
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
63
64
|
role = security_manager.find_role(name=role_name)
|
64
65
|
if not role:
|
65
66
|
raise NotFound(title="Role not found", detail=f"Role with name {role_name!r} was not found")
|
@@ -70,7 +71,7 @@ def get_role(*, role_name: str) -> APIResponse:
|
|
70
71
|
@format_parameters({"limit": check_limit})
|
71
72
|
def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None) -> APIResponse:
|
72
73
|
"""Get roles."""
|
73
|
-
security_manager = cast(
|
74
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
74
75
|
session = security_manager.get_session
|
75
76
|
total_entries = session.scalars(select(func.count(Role.id))).one()
|
76
77
|
direction = desc if order_by.startswith("-") else asc
|
@@ -80,8 +81,7 @@ def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None)
|
|
80
81
|
allowed_sort_attrs = ["role_id", "name"]
|
81
82
|
if order_by not in allowed_sort_attrs:
|
82
83
|
raise BadRequest(
|
83
|
-
detail=f"Ordering with '{order_by}' is disallowed or "
|
84
|
-
f"the attribute does not exist on the model"
|
84
|
+
detail=f"Ordering with '{order_by}' is disallowed or the attribute does not exist on the model"
|
85
85
|
)
|
86
86
|
|
87
87
|
query = select(Role)
|
@@ -98,7 +98,7 @@ def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None)
|
|
98
98
|
@format_parameters({"limit": check_limit})
|
99
99
|
def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
|
100
100
|
"""Get permissions."""
|
101
|
-
security_manager = cast(
|
101
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
102
102
|
session = security_manager.get_session
|
103
103
|
total_entries = session.scalars(select(func.count(Action.id))).one()
|
104
104
|
query = select(Action)
|
@@ -109,7 +109,7 @@ def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
|
|
109
109
|
@requires_access_custom_view("DELETE", permissions.RESOURCE_ROLE)
|
110
110
|
def delete_role(*, role_name: str) -> APIResponse:
|
111
111
|
"""Delete a role."""
|
112
|
-
security_manager = cast(
|
112
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
113
113
|
|
114
114
|
role = security_manager.find_role(name=role_name)
|
115
115
|
if not role:
|
@@ -121,7 +121,7 @@ def delete_role(*, role_name: str) -> APIResponse:
|
|
121
121
|
@requires_access_custom_view("PUT", permissions.RESOURCE_ROLE)
|
122
122
|
def patch_role(*, role_name: str, update_mask: UpdateMask = None) -> APIResponse:
|
123
123
|
"""Update a role."""
|
124
|
-
security_manager = cast(
|
124
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
125
125
|
body = request.json
|
126
126
|
try:
|
127
127
|
data = role_schema.load(body)
|
@@ -154,7 +154,7 @@ def patch_role(*, role_name: str, update_mask: UpdateMask = None) -> APIResponse
|
|
154
154
|
@requires_access_custom_view("POST", permissions.RESOURCE_ROLE)
|
155
155
|
def post_role() -> APIResponse:
|
156
156
|
"""Create a new role."""
|
157
|
-
security_manager = cast(
|
157
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
158
158
|
body = request.json
|
159
159
|
try:
|
160
160
|
data = role_schema.load(body)
|
@@ -25,9 +25,7 @@ from marshmallow import ValidationError
|
|
25
25
|
from sqlalchemy import asc, desc, func, select
|
26
26
|
from werkzeug.security import generate_password_hash
|
27
27
|
|
28
|
-
from airflow.
|
29
|
-
from airflow.api_connexion.parameters import check_limit, format_parameters
|
30
|
-
from airflow.api_connexion.security import requires_access_custom_view
|
28
|
+
from airflow.api_fastapi.app import get_auth_manager
|
31
29
|
from airflow.providers.fab.auth_manager.models import User
|
32
30
|
from airflow.providers.fab.auth_manager.schemas.user_schema import (
|
33
31
|
UserCollection,
|
@@ -35,19 +33,21 @@ from airflow.providers.fab.auth_manager.schemas.user_schema import (
|
|
35
33
|
user_collection_schema,
|
36
34
|
user_schema,
|
37
35
|
)
|
38
|
-
from airflow.providers.fab.
|
39
|
-
from airflow.
|
40
|
-
from airflow.www.
|
36
|
+
from airflow.providers.fab.www.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound, Unknown
|
37
|
+
from airflow.providers.fab.www.api_connexion.parameters import check_limit, format_parameters
|
38
|
+
from airflow.providers.fab.www.api_connexion.security import requires_access_custom_view
|
39
|
+
from airflow.providers.fab.www.security import permissions
|
41
40
|
|
42
41
|
if TYPE_CHECKING:
|
43
|
-
from airflow.
|
42
|
+
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
|
44
43
|
from airflow.providers.fab.auth_manager.models import Role
|
44
|
+
from airflow.providers.fab.www.api_connexion.types import APIResponse, UpdateMask
|
45
45
|
|
46
46
|
|
47
47
|
@requires_access_custom_view("GET", permissions.RESOURCE_USER)
|
48
48
|
def get_user(*, username: str) -> APIResponse:
|
49
49
|
"""Get a user."""
|
50
|
-
security_manager = cast(
|
50
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
51
51
|
user = security_manager.find_user(username=username)
|
52
52
|
if not user:
|
53
53
|
raise NotFound(title="User not found", detail=f"The User with username `{username}` was not found")
|
@@ -58,7 +58,7 @@ def get_user(*, username: str) -> APIResponse:
|
|
58
58
|
@format_parameters({"limit": check_limit})
|
59
59
|
def get_users(*, limit: int, order_by: str = "id", offset: str | None = None) -> APIResponse:
|
60
60
|
"""Get users."""
|
61
|
-
security_manager = cast(
|
61
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
62
62
|
session = security_manager.get_session
|
63
63
|
total_entries = session.execute(select(func.count(User.id))).scalar()
|
64
64
|
direction = desc if order_by.startswith("-") else asc
|
@@ -76,8 +76,7 @@ def get_users(*, limit: int, order_by: str = "id", offset: str | None = None) ->
|
|
76
76
|
]
|
77
77
|
if order_by not in allowed_sort_attrs:
|
78
78
|
raise BadRequest(
|
79
|
-
detail=f"Ordering with '{order_by}' is disallowed or "
|
80
|
-
f"the attribute does not exist on the model"
|
79
|
+
detail=f"Ordering with '{order_by}' is disallowed or the attribute does not exist on the model"
|
81
80
|
)
|
82
81
|
|
83
82
|
query = select(User).order_by(direction(getattr(User, order_param))).offset(offset).limit(limit)
|
@@ -94,7 +93,7 @@ def post_user() -> APIResponse:
|
|
94
93
|
except ValidationError as e:
|
95
94
|
raise BadRequest(detail=str(e.messages))
|
96
95
|
|
97
|
-
security_manager = cast(
|
96
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
98
97
|
username = data["username"]
|
99
98
|
email = data["email"]
|
100
99
|
|
@@ -137,7 +136,7 @@ def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
|
|
137
136
|
except ValidationError as e:
|
138
137
|
raise BadRequest(detail=str(e.messages))
|
139
138
|
|
140
|
-
security_manager = cast(
|
139
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
141
140
|
|
142
141
|
user = security_manager.find_user(username=username)
|
143
142
|
if user is None:
|
@@ -201,7 +200,7 @@ def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
|
|
201
200
|
@requires_access_custom_view("DELETE", permissions.RESOURCE_USER)
|
202
201
|
def delete_user(*, username: str) -> APIResponse:
|
203
202
|
"""Delete a user."""
|
204
|
-
security_manager = cast(
|
203
|
+
security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
|
205
204
|
|
206
205
|
user = security_manager.find_user(username=username)
|
207
206
|
if user is None:
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
from airflow.api_fastapi.core_api.base import BaseModel
|
20
|
+
|
21
|
+
|
22
|
+
class LoginResponse(BaseModel):
|
23
|
+
"""API Token serializer for responses."""
|
24
|
+
|
25
|
+
access_token: str
|
26
|
+
|
27
|
+
|
28
|
+
class LoginBody(BaseModel):
|
29
|
+
"""API Token serializer for requests."""
|
30
|
+
|
31
|
+
username: str
|
32
|
+
password: str
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
@@ -0,0 +1,153 @@
|
|
1
|
+
openapi: 3.1.0
|
2
|
+
info:
|
3
|
+
title: FAB auth manager API
|
4
|
+
description: This is FAB auth manager API. This API is only available if the auth
|
5
|
+
manager used in the Airflow environment is FAB auth manager. This API provides
|
6
|
+
endpoints to manage users and permissions managed by the FAB auth manager.
|
7
|
+
version: 0.1.0
|
8
|
+
paths:
|
9
|
+
/token:
|
10
|
+
post:
|
11
|
+
tags:
|
12
|
+
- FabAuthManager
|
13
|
+
summary: Create Token
|
14
|
+
description: Generate a new API token.
|
15
|
+
operationId: create_token
|
16
|
+
requestBody:
|
17
|
+
content:
|
18
|
+
application/json:
|
19
|
+
schema:
|
20
|
+
$ref: '#/components/schemas/LoginBody'
|
21
|
+
required: true
|
22
|
+
responses:
|
23
|
+
'201':
|
24
|
+
description: Successful Response
|
25
|
+
content:
|
26
|
+
application/json:
|
27
|
+
schema:
|
28
|
+
$ref: '#/components/schemas/LoginResponse'
|
29
|
+
'400':
|
30
|
+
description: Bad Request
|
31
|
+
content:
|
32
|
+
application/json:
|
33
|
+
schema:
|
34
|
+
$ref: '#/components/schemas/HTTPExceptionResponse'
|
35
|
+
'401':
|
36
|
+
description: Unauthorized
|
37
|
+
content:
|
38
|
+
application/json:
|
39
|
+
schema:
|
40
|
+
$ref: '#/components/schemas/HTTPExceptionResponse'
|
41
|
+
'422':
|
42
|
+
description: Validation Error
|
43
|
+
content:
|
44
|
+
application/json:
|
45
|
+
schema:
|
46
|
+
$ref: '#/components/schemas/HTTPValidationError'
|
47
|
+
/token/cli:
|
48
|
+
post:
|
49
|
+
tags:
|
50
|
+
- FabAuthManager
|
51
|
+
summary: Create Token Cli
|
52
|
+
description: Generate a new CLI API token.
|
53
|
+
operationId: create_token_cli
|
54
|
+
requestBody:
|
55
|
+
content:
|
56
|
+
application/json:
|
57
|
+
schema:
|
58
|
+
$ref: '#/components/schemas/LoginBody'
|
59
|
+
required: true
|
60
|
+
responses:
|
61
|
+
'201':
|
62
|
+
description: Successful Response
|
63
|
+
content:
|
64
|
+
application/json:
|
65
|
+
schema:
|
66
|
+
$ref: '#/components/schemas/LoginResponse'
|
67
|
+
'400':
|
68
|
+
description: Bad Request
|
69
|
+
content:
|
70
|
+
application/json:
|
71
|
+
schema:
|
72
|
+
$ref: '#/components/schemas/HTTPExceptionResponse'
|
73
|
+
'401':
|
74
|
+
description: Unauthorized
|
75
|
+
content:
|
76
|
+
application/json:
|
77
|
+
schema:
|
78
|
+
$ref: '#/components/schemas/HTTPExceptionResponse'
|
79
|
+
'422':
|
80
|
+
description: Validation Error
|
81
|
+
content:
|
82
|
+
application/json:
|
83
|
+
schema:
|
84
|
+
$ref: '#/components/schemas/HTTPValidationError'
|
85
|
+
components:
|
86
|
+
schemas:
|
87
|
+
HTTPExceptionResponse:
|
88
|
+
properties:
|
89
|
+
detail:
|
90
|
+
anyOf:
|
91
|
+
- type: string
|
92
|
+
- additionalProperties: true
|
93
|
+
type: object
|
94
|
+
title: Detail
|
95
|
+
type: object
|
96
|
+
required:
|
97
|
+
- detail
|
98
|
+
title: HTTPExceptionResponse
|
99
|
+
description: HTTPException Model used for error response.
|
100
|
+
HTTPValidationError:
|
101
|
+
properties:
|
102
|
+
detail:
|
103
|
+
items:
|
104
|
+
$ref: '#/components/schemas/ValidationError'
|
105
|
+
type: array
|
106
|
+
title: Detail
|
107
|
+
type: object
|
108
|
+
title: HTTPValidationError
|
109
|
+
LoginBody:
|
110
|
+
properties:
|
111
|
+
username:
|
112
|
+
type: string
|
113
|
+
title: Username
|
114
|
+
password:
|
115
|
+
type: string
|
116
|
+
title: Password
|
117
|
+
type: object
|
118
|
+
required:
|
119
|
+
- username
|
120
|
+
- password
|
121
|
+
title: LoginBody
|
122
|
+
description: API Token serializer for requests.
|
123
|
+
LoginResponse:
|
124
|
+
properties:
|
125
|
+
access_token:
|
126
|
+
type: string
|
127
|
+
title: Access Token
|
128
|
+
type: object
|
129
|
+
required:
|
130
|
+
- access_token
|
131
|
+
title: LoginResponse
|
132
|
+
description: API Token serializer for responses.
|
133
|
+
ValidationError:
|
134
|
+
properties:
|
135
|
+
loc:
|
136
|
+
items:
|
137
|
+
anyOf:
|
138
|
+
- type: string
|
139
|
+
- type: integer
|
140
|
+
type: array
|
141
|
+
title: Location
|
142
|
+
msg:
|
143
|
+
type: string
|
144
|
+
title: Message
|
145
|
+
type:
|
146
|
+
type: string
|
147
|
+
title: Error Type
|
148
|
+
type: object
|
149
|
+
required:
|
150
|
+
- loc
|
151
|
+
- msg
|
152
|
+
- type
|
153
|
+
title: ValidationError
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
from starlette import status
|
20
|
+
|
21
|
+
from airflow.api_fastapi.common.router import AirflowRouter
|
22
|
+
from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc
|
23
|
+
from airflow.configuration import conf
|
24
|
+
from airflow.providers.fab.auth_manager.api_fastapi.datamodels.login import LoginBody, LoginResponse
|
25
|
+
from airflow.providers.fab.auth_manager.api_fastapi.services.login import FABAuthManagerLogin
|
26
|
+
|
27
|
+
login_router = AirflowRouter(tags=["FabAuthManager"])
|
28
|
+
|
29
|
+
|
30
|
+
@login_router.post(
|
31
|
+
"/token",
|
32
|
+
response_model=LoginResponse,
|
33
|
+
status_code=status.HTTP_201_CREATED,
|
34
|
+
responses=create_openapi_http_exception_doc([status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED]),
|
35
|
+
)
|
36
|
+
def create_token(body: LoginBody) -> LoginResponse:
|
37
|
+
"""Generate a new API token."""
|
38
|
+
return FABAuthManagerLogin.create_token(body=body)
|
39
|
+
|
40
|
+
|
41
|
+
@login_router.post(
|
42
|
+
"/token/cli",
|
43
|
+
response_model=LoginResponse,
|
44
|
+
status_code=status.HTTP_201_CREATED,
|
45
|
+
responses=create_openapi_http_exception_doc([status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED]),
|
46
|
+
)
|
47
|
+
def create_token_cli(body: LoginBody) -> LoginResponse:
|
48
|
+
"""Generate a new CLI API token."""
|
49
|
+
return FABAuthManagerLogin.create_token(
|
50
|
+
body=body, expiration_time_in_seconds=conf.getint("api_auth", "jwt_cli_expiration_time")
|
51
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|