apache-airflow-providers-fab 1.5.3rc1__py3-none-any.whl → 2.0.0b1__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.
Files changed (101) hide show
  1. airflow/providers/fab/LICENSE +0 -52
  2. airflow/providers/fab/__init__.py +3 -3
  3. airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +3 -3
  4. airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py +4 -4
  5. airflow/providers/fab/auth_manager/api/auth/backend/session.py +1 -1
  6. airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +14 -13
  7. airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +12 -12
  8. airflow/providers/fab/auth_manager/api_fastapi/__init__.py +16 -0
  9. airflow/providers/fab/auth_manager/api_fastapi/datamodels/__init__.py +16 -0
  10. airflow/providers/fab/auth_manager/api_fastapi/datamodels/login.py +32 -0
  11. airflow/providers/fab/auth_manager/api_fastapi/openapi/__init__.py +16 -0
  12. airflow/providers/fab/auth_manager/api_fastapi/openapi/v1-generated.yaml +152 -0
  13. airflow/providers/fab/auth_manager/api_fastapi/routes/__init__.py +16 -0
  14. airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +51 -0
  15. airflow/providers/fab/auth_manager/api_fastapi/services/__init__.py +16 -0
  16. airflow/providers/fab/auth_manager/api_fastapi/services/login.py +58 -0
  17. airflow/providers/fab/auth_manager/cli_commands/db_command.py +2 -4
  18. airflow/providers/fab/auth_manager/cli_commands/user_command.py +2 -2
  19. airflow/providers/fab/auth_manager/cli_commands/utils.py +10 -9
  20. airflow/providers/fab/auth_manager/fab_auth_manager.py +231 -126
  21. airflow/providers/fab/auth_manager/models/__init__.py +1 -1
  22. airflow/providers/fab/auth_manager/models/anonymous_user.py +1 -1
  23. airflow/providers/fab/auth_manager/models/db.py +22 -5
  24. airflow/providers/fab/auth_manager/schemas/user_schema.py +1 -1
  25. airflow/providers/fab/auth_manager/security_manager/override.py +71 -632
  26. airflow/providers/fab/auth_manager/views/permissions.py +1 -1
  27. airflow/providers/fab/auth_manager/views/roles_list.py +1 -1
  28. airflow/providers/fab/auth_manager/views/user.py +1 -1
  29. airflow/providers/fab/auth_manager/views/user_edit.py +1 -1
  30. airflow/providers/fab/auth_manager/views/user_stats.py +1 -1
  31. airflow/providers/fab/get_provider_info.py +22 -16
  32. airflow/providers/fab/www/airflow_flask_app.py +31 -0
  33. airflow/providers/fab/www/api_connexion/__init__.py +17 -0
  34. airflow/providers/fab/www/api_connexion/exceptions.py +197 -0
  35. airflow/providers/fab/www/api_connexion/parameters.py +131 -0
  36. airflow/providers/fab/www/api_connexion/security.py +84 -0
  37. airflow/providers/fab/www/api_connexion/types.py +30 -0
  38. airflow/providers/fab/www/app.py +112 -0
  39. airflow/providers/fab/www/auth.py +350 -0
  40. airflow/providers/fab/www/constants.py +28 -0
  41. airflow/providers/fab/www/extensions/__init__.py +16 -0
  42. airflow/providers/fab/www/extensions/init_appbuilder.py +602 -0
  43. airflow/providers/fab/www/extensions/init_jinja_globals.py +82 -0
  44. airflow/providers/fab/www/extensions/init_manifest_files.py +61 -0
  45. airflow/providers/fab/www/extensions/init_security.py +61 -0
  46. airflow/providers/fab/www/extensions/init_session.py +64 -0
  47. airflow/providers/fab/www/extensions/init_views.py +177 -0
  48. airflow/providers/fab/www/package-lock.json +10127 -0
  49. airflow/providers/fab/www/package.json +81 -0
  50. airflow/providers/fab/www/security/__init__.py +17 -0
  51. airflow/providers/fab/www/security/permissions.py +126 -0
  52. airflow/providers/fab/www/security_appless.py +44 -0
  53. airflow/providers/fab/www/security_manager.py +122 -0
  54. airflow/providers/fab/www/session.py +41 -0
  55. airflow/providers/fab/www/static/css/bootstrap-theme.css +6215 -0
  56. airflow/providers/fab/www/static/css/flash.css +57 -0
  57. airflow/providers/fab/www/static/css/loading-dots.css +60 -0
  58. airflow/providers/fab/www/static/css/main.css +676 -0
  59. airflow/providers/fab/www/static/css/material-icons.css +84 -0
  60. airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.css +33 -0
  61. airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.js +1 -0
  62. airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.css +18 -0
  63. airflow/providers/fab/www/static/dist/flash.137b30cff85b5588e661.js +1 -0
  64. airflow/providers/fab/www/static/dist/jquery-ui.min.css +5 -0
  65. airflow/providers/fab/www/static/dist/jquery-ui.min.js +2 -0
  66. airflow/providers/fab/www/static/dist/jquery-ui.min.js.LICENSE.txt +4 -0
  67. airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.css +18 -0
  68. airflow/providers/fab/www/static/dist/loadingDots.48ab7d5b04e66f2686b0.js +1 -0
  69. airflow/providers/fab/www/static/dist/main.ec1d38d994d72bb083cd.css +18 -0
  70. airflow/providers/fab/www/static/dist/main.ec1d38d994d72bb083cd.js +2 -0
  71. airflow/providers/fab/www/static/dist/main.ec1d38d994d72bb083cd.js.LICENSE.txt +18 -0
  72. airflow/providers/fab/www/static/dist/manifest.json +17 -0
  73. airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.css +18 -0
  74. airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.js +1 -0
  75. airflow/providers/fab/www/static/dist/moment.4d28b37c229bdfc54575.js +2 -0
  76. airflow/providers/fab/www/static/dist/moment.4d28b37c229bdfc54575.js.LICENSE.txt +11 -0
  77. airflow/providers/fab/www/static/dist/oss-licenses.json +29 -0
  78. airflow/providers/fab/www/static/js/datetime_utils.js +134 -0
  79. airflow/providers/fab/www/static/js/main.js +324 -0
  80. airflow/providers/fab/www/static/sort_asc.png +0 -0
  81. airflow/providers/fab/www/static/sort_both.png +0 -0
  82. airflow/providers/fab/www/static/sort_desc.png +0 -0
  83. airflow/providers/fab/www/templates/airflow/_messages.html +30 -0
  84. airflow/providers/fab/www/templates/airflow/error.html +35 -0
  85. airflow/providers/fab/www/templates/airflow/main.html +78 -0
  86. airflow/providers/fab/www/templates/airflow/traceback.html +53 -0
  87. airflow/providers/fab/www/templates/appbuilder/flash.html +34 -0
  88. airflow/providers/fab/www/templates/appbuilder/index.html +20 -0
  89. airflow/providers/fab/www/templates/appbuilder/navbar.html +60 -0
  90. airflow/providers/fab/www/templates/appbuilder/navbar_menu.html +60 -0
  91. airflow/providers/fab/www/templates/appbuilder/navbar_right.html +64 -0
  92. airflow/providers/fab/www/utils.py +272 -0
  93. airflow/providers/fab/www/views.py +129 -0
  94. airflow/providers/fab/www/webpack.config.js +213 -0
  95. {apache_airflow_providers_fab-1.5.3rc1.dist-info → apache_airflow_providers_fab-2.0.0b1.dist-info}/METADATA +18 -36
  96. apache_airflow_providers_fab-2.0.0b1.dist-info/RECORD +122 -0
  97. {apache_airflow_providers_fab-1.5.3rc1.dist-info → apache_airflow_providers_fab-2.0.0b1.dist-info}/WHEEL +1 -1
  98. airflow/providers/fab/auth_manager/decorators/auth.py +0 -126
  99. apache_airflow_providers_fab-1.5.3rc1.dist-info/RECORD +0 -51
  100. /airflow/providers/fab/{auth_manager/decorators → www}/__init__.py +0 -0
  101. {apache_airflow_providers_fab-1.5.3rc1.dist-info → apache_airflow_providers_fab-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "airflow-www",
3
+ "version": "1.0.0",
4
+ "description": "Apache Airflow is a platform to programmatically author, schedule and monitor workflows.",
5
+ "scripts": {
6
+ "test": "jest",
7
+ "dev": "NODE_ENV=development webpack --watch --progress --devtool eval-cheap-source-map --mode development",
8
+ "prod": "NODE_ENV=production node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --mode production --progress",
9
+ "build": "NODE_ENV=production webpack --progress --mode production",
10
+ "lint": "eslint --ignore-path=.eslintignore --max-warnings=0 --ext .js .",
11
+ "lint:fix": "eslint --fix --ignore-path=.eslintignore --ext .js .",
12
+ "format": "yarn prettier --write ."
13
+ },
14
+ "author": "Apache",
15
+ "license": "Apache-2.0",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/apache/airflow.git"
19
+ },
20
+ "homepage": "https://airflow.apache.org/",
21
+ "keywords": [
22
+ "big",
23
+ "data",
24
+ "workflow",
25
+ "airflow",
26
+ "database",
27
+ "flask"
28
+ ],
29
+ "browserslist": {
30
+ "production": [
31
+ ">0.2%",
32
+ "not dead",
33
+ "not op_mini all"
34
+ ],
35
+ "development": [
36
+ "last 1 chrome version",
37
+ "last 1 firefox version",
38
+ "last 1 safari version"
39
+ ]
40
+ },
41
+ "devDependencies": {
42
+ "@babel/core": "^7.24.7",
43
+ "@babel/eslint-parser": "^7.24.7",
44
+ "@babel/plugin-transform-runtime": "^7.24.7",
45
+ "@babel/preset-env": "^7.24.7",
46
+ "babel-jest": "^27.3.1",
47
+ "babel-loader": "^9.1.0",
48
+ "clean-webpack-plugin": "^3.0.0",
49
+ "copy-webpack-plugin": "^6.0.3",
50
+ "css-loader": "5.2.7",
51
+ "css-minimizer-webpack-plugin": "^4.0.0",
52
+ "eslint": "^8.6.0",
53
+ "eslint-config-prettier": "^8.6.0",
54
+ "eslint-plugin-html": "^6.0.2",
55
+ "eslint-plugin-import": "^2.27.5",
56
+ "eslint-plugin-node": "^11.1.0",
57
+ "eslint-plugin-standard": "^4.0.1",
58
+ "file-loader": "^6.0.0",
59
+ "imports-loader": "^1.1.0",
60
+ "mini-css-extract-plugin": "^1.6.2",
61
+ "moment": "^2.29.4",
62
+ "moment-locales-webpack-plugin": "^1.2.0",
63
+ "prettier": "^2.8.4",
64
+ "style-loader": "^1.2.1",
65
+ "stylelint": "^15.10.1",
66
+ "terser-webpack-plugin": "<6.0.0",
67
+ "url-loader": "4.1.0",
68
+ "web-worker": "^1.2.0",
69
+ "webpack": "^5.94.0",
70
+ "webpack-cli": "^4.0.0",
71
+ "webpack-license-plugin": "^4.2.1",
72
+ "webpack-manifest-plugin": "^4.0.0"
73
+ },
74
+ "dependencies": {
75
+ "jquery-ui": "^1.14.1",
76
+ "moment-timezone": "^0.5.43"
77
+ },
78
+ "resolutions": {
79
+ "moment-timezone": ">=0.5.35"
80
+ }
81
+ }
@@ -0,0 +1,17 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
@@ -0,0 +1,126 @@
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 typing import TypedDict
20
+
21
+ # Resource Constants
22
+ RESOURCE_ACTION = "Permissions"
23
+ RESOURCE_ADMIN_MENU = "Admin"
24
+ RESOURCE_AUDIT_LOG = "Audit Logs"
25
+ RESOURCE_BACKFILL = "Backfills"
26
+ RESOURCE_BROWSE_MENU = "Browse"
27
+ RESOURCE_CONFIG = "Configurations"
28
+ RESOURCE_CONNECTION = "Connections"
29
+ RESOURCE_DAG = "DAGs"
30
+ RESOURCE_DAG_CODE = "DAG Code"
31
+ RESOURCE_DAG_DEPENDENCIES = "DAG Dependencies"
32
+ RESOURCE_DAG_PREFIX = "DAG:"
33
+ RESOURCE_DAG_RUN = "DAG Runs"
34
+ RESOURCE_DAG_RUN_PREFIX = "DAG Run:"
35
+ RESOURCE_DAG_VERSION = "DAG Versions"
36
+ RESOURCE_DAG_WARNING = "DAG Warnings"
37
+ RESOURCE_CLUSTER_ACTIVITY = "Cluster Activity"
38
+ RESOURCE_ASSET = "Assets"
39
+ RESOURCE_ASSET_ALIAS = "Asset Aliases"
40
+ RESOURCE_DOCS = "Documentation"
41
+ RESOURCE_DOCS_MENU = "Docs"
42
+ RESOURCE_IMPORT_ERROR = "ImportError"
43
+ RESOURCE_JOB = "Jobs"
44
+ RESOURCE_MY_PASSWORD = "My Password"
45
+ RESOURCE_MY_PROFILE = "My Profile"
46
+ RESOURCE_PASSWORD = "Passwords"
47
+ RESOURCE_PERMISSION = "Permission Views" # Refers to a Perm <-> View mapping, not an MVC View.
48
+ RESOURCE_PLUGIN = "Plugins"
49
+ RESOURCE_POOL = "Pools"
50
+ RESOURCE_PROVIDER = "Providers"
51
+ RESOURCE_RESOURCE = "View Menus"
52
+ RESOURCE_ROLE = "Roles"
53
+ RESOURCE_SLA_MISS = "SLA Misses"
54
+ RESOURCE_TASK_INSTANCE = "Task Instances"
55
+ RESOURCE_TASK_LOG = "Task Logs"
56
+ RESOURCE_TASK_RESCHEDULE = "Task Reschedules"
57
+ RESOURCE_TRIGGER = "Triggers"
58
+ RESOURCE_USER = "Users"
59
+ RESOURCE_USER_STATS_CHART = "User Stats Chart"
60
+ RESOURCE_VARIABLE = "Variables"
61
+ RESOURCE_WEBSITE = "Website"
62
+ RESOURCE_XCOM = "XComs"
63
+
64
+ # Action Constants
65
+ ACTION_CAN_CREATE = "can_create"
66
+ ACTION_CAN_READ = "can_read"
67
+ ACTION_CAN_EDIT = "can_edit"
68
+ ACTION_CAN_DELETE = "can_delete"
69
+ ACTION_CAN_ACCESS_MENU = "menu_access"
70
+ DEPRECATED_ACTION_CAN_DAG_READ = "can_dag_read"
71
+ DEPRECATED_ACTION_CAN_DAG_EDIT = "can_dag_edit"
72
+
73
+
74
+ class ResourceDetails(TypedDict):
75
+ """Details of a resource (actions and prefix)."""
76
+
77
+ actions: set[str]
78
+ prefix: str
79
+
80
+
81
+ # Keeping DAG_ACTIONS to keep the compatibility with outdated versions of FAB provider
82
+ DAG_ACTIONS = {ACTION_CAN_READ, ACTION_CAN_EDIT, ACTION_CAN_DELETE}
83
+
84
+ RESOURCE_DETAILS_MAP = {
85
+ RESOURCE_DAG: ResourceDetails(
86
+ actions={ACTION_CAN_READ, ACTION_CAN_EDIT, ACTION_CAN_DELETE}, prefix=RESOURCE_DAG_PREFIX
87
+ ),
88
+ RESOURCE_DAG_RUN: ResourceDetails(
89
+ actions={ACTION_CAN_READ, ACTION_CAN_CREATE, ACTION_CAN_DELETE, ACTION_CAN_ACCESS_MENU},
90
+ prefix=RESOURCE_DAG_RUN_PREFIX,
91
+ ),
92
+ }
93
+ PREFIX_LIST = [details["prefix"] for details in RESOURCE_DETAILS_MAP.values()]
94
+ PREFIX_RESOURCES_MAP = {details["prefix"]: resource for resource, details in RESOURCE_DETAILS_MAP.items()}
95
+
96
+
97
+ def resource_name(root_dag_id: str, resource: str) -> str:
98
+ """
99
+ Return the resource name for a DAG id.
100
+
101
+ Note that since a sub-DAG should follow the permission of its
102
+ parent DAG, you should pass ``DagModel.root_dag_id`` to this function,
103
+ for a subdag. A normal dag should pass the ``DagModel.dag_id``.
104
+ """
105
+ if root_dag_id in RESOURCE_DETAILS_MAP.keys():
106
+ return root_dag_id
107
+ if root_dag_id.startswith(tuple(PREFIX_RESOURCES_MAP.keys())):
108
+ return root_dag_id
109
+ return f"{RESOURCE_DETAILS_MAP[resource]['prefix']}{root_dag_id}"
110
+
111
+
112
+ def resource_name_for_dag(root_dag_id: str) -> str:
113
+ """
114
+ Return the resource name for a DAG id.
115
+
116
+ Note that since a sub-DAG should follow the permission of its
117
+ parent DAG, you should pass ``DagModel.root_dag_id`` to this function,
118
+ for a subdag. A normal dag should pass the ``DagModel.dag_id``.
119
+
120
+ Note: This function is kept for backwards compatibility.
121
+ """
122
+ if root_dag_id == RESOURCE_DAG:
123
+ return root_dag_id
124
+ if root_dag_id.startswith(RESOURCE_DAG_PREFIX):
125
+ return root_dag_id
126
+ return f"{RESOURCE_DAG_PREFIX}{root_dag_id}"
@@ -0,0 +1,44 @@
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 typing import TYPE_CHECKING
20
+
21
+ from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
22
+
23
+ if TYPE_CHECKING:
24
+ from sqlalchemy.orm import Session
25
+
26
+
27
+ class FakeAppBuilder:
28
+ """
29
+ Stand-in class to replace a Flask App Builder.
30
+
31
+ The only purpose is to provide the ``self.appbuilder.get_session`` interface
32
+ for ``ApplessAirflowSecurityManager`` so it can be used without a real Flask
33
+ app, which is slow to create.
34
+ """
35
+
36
+ def __init__(self, session: Session | None = None) -> None:
37
+ self.get_session = session
38
+
39
+
40
+ class ApplessAirflowSecurityManager(FabAirflowSecurityManagerOverride):
41
+ """Security Manager that doesn't need the whole flask app."""
42
+
43
+ def __init__(self, session: Session | None = None):
44
+ self.appbuilder = FakeAppBuilder(session)
@@ -0,0 +1,122 @@
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 typing import Callable
20
+
21
+ from flask import g
22
+ from flask_limiter import Limiter
23
+ from flask_limiter.util import get_remote_address
24
+
25
+ from airflow.api_fastapi.app import get_auth_manager
26
+ from airflow.providers.fab.www.utils import CustomSQLAInterface, get_method_from_fab_action_map
27
+ from airflow.utils.log.logging_mixin import LoggingMixin
28
+
29
+ EXISTING_ROLES = {
30
+ "Admin",
31
+ "Viewer",
32
+ "User",
33
+ "Op",
34
+ "Public",
35
+ }
36
+
37
+
38
+ class AirflowSecurityManagerV2(LoggingMixin):
39
+ """
40
+ Minimal security manager needed to run a Flask application.
41
+
42
+ This one is used to run the Flask application needed to run Airflow 2 plugins unless Fab auth manager
43
+ is configured in the environment. In that case, ``FabAirflowSecurityManagerOverride`` is used.
44
+ """
45
+
46
+ def __init__(self, appbuilder) -> None:
47
+ super().__init__()
48
+ self.appbuilder = appbuilder
49
+
50
+ # Setup Flask-Limiter
51
+ self.limiter = self.create_limiter()
52
+
53
+ # Go and fix up the SQLAInterface used from the stock one to our subclass.
54
+ # This is needed to support the "hack" where we had to edit
55
+ # FieldConverter.conversion_table in place in utils
56
+ for attr in dir(self):
57
+ if attr.endswith("view"):
58
+ view = getattr(self, attr, None)
59
+ if view and getattr(view, "datamodel", None):
60
+ view.datamodel = CustomSQLAInterface(view.datamodel.obj)
61
+
62
+ @staticmethod
63
+ def before_request():
64
+ """Run hook before request."""
65
+ if hasattr(get_auth_manager(), "get_user"):
66
+ g.user = get_auth_manager().get_user()
67
+
68
+ def create_limiter(self) -> Limiter:
69
+ app = self.appbuilder.get_app
70
+ limiter = Limiter(key_func=app.config.get("RATELIMIT_KEY_FUNC", get_remote_address))
71
+ limiter.init_app(app)
72
+ return limiter
73
+
74
+ def has_access(
75
+ self, action_name: str, resource_name: str, user=None, resource_pk: str | None = None
76
+ ) -> bool:
77
+ """
78
+ Verify whether a given user could perform a certain action on the given resource.
79
+
80
+ Example actions might include can_read, can_write, can_delete, etc.
81
+
82
+ This function is called by FAB when accessing a view. See
83
+ https://github.com/dpgaspar/Flask-AppBuilder/blob/c6fecdc551629e15467fde5d06b4437379d90592/flask_appbuilder/security/decorators.py#L134
84
+
85
+ :param action_name: action_name on resource (e.g can_read, can_edit).
86
+ :param resource_name: name of view-menu or resource.
87
+ :param user: user
88
+ :param resource_pk: the resource primary key (e.g. the connection ID)
89
+ :return: Whether user could perform certain action on the resource.
90
+ """
91
+ if not user:
92
+ user = g.user
93
+
94
+ is_authorized_method = self._get_auth_manager_is_authorized_method(resource_name)
95
+ return is_authorized_method(action_name, resource_pk, user)
96
+
97
+ def add_limit_view(self, baseview):
98
+ if not baseview.limits:
99
+ return
100
+
101
+ for limit in baseview.limits:
102
+ self.limiter.limit(
103
+ limit_value=limit.limit_value,
104
+ key_func=limit.key_func,
105
+ per_method=limit.per_method,
106
+ methods=limit.methods,
107
+ error_message=limit.error_message,
108
+ exempt_when=limit.exempt_when,
109
+ override_defaults=limit.override_defaults,
110
+ deduct_when=limit.deduct_when,
111
+ on_breach=limit.on_breach,
112
+ cost=limit.cost,
113
+ )(baseview.blueprint)
114
+
115
+ def _get_auth_manager_is_authorized_method(self, fab_resource_name: str) -> Callable:
116
+ # The user is trying to access a page specific to the auth manager
117
+ # (e.g. the user list view in FabAuthManager) or a page defined in a plugin
118
+ return lambda action, resource_pk, user: get_auth_manager().is_authorized_custom_view(
119
+ method=get_method_from_fab_action_map().get(action, action),
120
+ resource_name=fab_resource_name,
121
+ user=user,
122
+ )
@@ -0,0 +1,41 @@
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 flask import request
20
+ from flask.sessions import SecureCookieSessionInterface
21
+ from flask_session.sessions import SqlAlchemySessionInterface
22
+
23
+
24
+ class SessionExemptMixin:
25
+ """Exempt certain blueprints/paths from autogenerated sessions."""
26
+
27
+ def save_session(self, *args, **kwargs):
28
+ """Prevent creating session from REST API and health requests."""
29
+ if request.blueprint == "/api/v1":
30
+ return None
31
+ if request.path == "/health":
32
+ return None
33
+ return super().save_session(*args, **kwargs)
34
+
35
+
36
+ class AirflowDatabaseSessionInterface(SessionExemptMixin, SqlAlchemySessionInterface):
37
+ """Session interface that exempts some routes and stores session data in the database."""
38
+
39
+
40
+ class AirflowSecureCookieSessionInterface(SessionExemptMixin, SecureCookieSessionInterface):
41
+ """Session interface that exempts some routes and stores session data in a signed cookie."""