We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CORS access_control_allow_origin header never returned
### Apache Airflow version

2.2.2 (latest released)

### What happened

To fix CORS problem added the [access_control_allow_headers](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-headers), [access_control_allow_methods](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-methods), [access_control_allow_origins](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-origins) variables to the 2.2.2 docker-compose file provided in documentation. Both header, and methods returns with the correct value, but origins never does.

### What you expected to happen

The CORS response returning with provided origin header value.

### How to reproduce

Download the latest docker-compose from documentation add the following lines:
`AIRFLOW__API__ACCESS_CONTROL_ALLOW_HEADERS: 'content-type, origin, authorization, accept'`
`AIRFLOW__API__ACCESS_CONTROL_ALLOW_METHODS: 'GET, POST, OPTIONS, DELETE'`
`AIRFLOW__API__ACCESS_CONTROL_ALLOW_ORIGINS: '*'`

run and call with a CORS preflight

### Operating System

Windows 11

### Versions of Apache Airflow Providers

_No response_

### Deployment

Docker-Compose

### Deployment details

_No response_

### Anything else

It's repeatable regardless of ORIGINS value. There was a name change on this variable that's possibly not handled.
On 2.1.4 the same works without problems.

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `airflow/www/extensions/init_views.py`
Content:
```
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 
18 import logging
19 import warnings
20 from os import path
21 
22 from connexion import App, ProblemException
23 from flask import Flask, request
24 
25 from airflow.api_connexion.exceptions import common_error_handler
26 from airflow.configuration import conf
27 from airflow.security import permissions
28 from airflow.www.views import lazy_add_provider_discovered_options_to_connection_form
29 
30 log = logging.getLogger(__name__)
31 
32 # airflow/www/extensions/init_views.py => airflow/
33 ROOT_APP_DIR = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
34 
35 
36 def init_flash_views(app):
37     """Init main app view - redirect to FAB"""
38     from airflow.www.blueprints import routes
39 
40     app.register_blueprint(routes)
41 
42 
43 def init_appbuilder_views(app):
44     """Initialize Web UI views"""
45     appbuilder = app.appbuilder
46     from airflow.www import views
47 
48     # Remove the session from scoped_session registry to avoid
49     # reusing a session with a disconnected connection
50     appbuilder.session.remove()
51     appbuilder.add_view_no_menu(views.AutocompleteView())
52     appbuilder.add_view_no_menu(views.Airflow())
53     appbuilder.add_view(
54         views.DagRunModelView,
55         permissions.RESOURCE_DAG_RUN,
56         category=permissions.RESOURCE_BROWSE_MENU,
57         category_icon="fa-globe",
58     )
59     appbuilder.add_view(
60         views.JobModelView, permissions.RESOURCE_JOB, category=permissions.RESOURCE_BROWSE_MENU
61     )
62     appbuilder.add_view(
63         views.LogModelView, permissions.RESOURCE_AUDIT_LOG, category=permissions.RESOURCE_BROWSE_MENU
64     )
65     appbuilder.add_view(
66         views.VariableModelView, permissions.RESOURCE_VARIABLE, category=permissions.RESOURCE_ADMIN_MENU
67     )
68     appbuilder.add_view(
69         views.TaskInstanceModelView,
70         permissions.RESOURCE_TASK_INSTANCE,
71         category=permissions.RESOURCE_BROWSE_MENU,
72     )
73     appbuilder.add_view(
74         views.TaskRescheduleModelView,
75         permissions.RESOURCE_TASK_RESCHEDULE,
76         category=permissions.RESOURCE_BROWSE_MENU,
77     )
78     appbuilder.add_view(
79         views.TriggerModelView,
80         permissions.RESOURCE_TRIGGER,
81         category=permissions.RESOURCE_BROWSE_MENU,
82     )
83     appbuilder.add_view(
84         views.ConfigurationView,
85         permissions.RESOURCE_CONFIG,
86         category=permissions.RESOURCE_ADMIN_MENU,
87         category_icon="fa-user",
88     )
89     appbuilder.add_view(
90         views.ConnectionModelView, permissions.RESOURCE_CONNECTION, category=permissions.RESOURCE_ADMIN_MENU
91     )
92     appbuilder.add_view(
93         views.SlaMissModelView, permissions.RESOURCE_SLA_MISS, category=permissions.RESOURCE_BROWSE_MENU
94     )
95     appbuilder.add_view(
96         views.PluginView, permissions.RESOURCE_PLUGIN, category=permissions.RESOURCE_ADMIN_MENU
97     )
98     appbuilder.add_view(
99         views.ProviderView, permissions.RESOURCE_PROVIDER, category=permissions.RESOURCE_ADMIN_MENU
100     )
101     appbuilder.add_view(
102         views.PoolModelView, permissions.RESOURCE_POOL, category=permissions.RESOURCE_ADMIN_MENU
103     )
104     appbuilder.add_view(
105         views.XComModelView, permissions.RESOURCE_XCOM, category=permissions.RESOURCE_ADMIN_MENU
106     )
107     appbuilder.add_view(
108         views.DagDependenciesView,
109         permissions.RESOURCE_DAG_DEPENDENCIES,
110         category=permissions.RESOURCE_BROWSE_MENU,
111     )
112     # add_view_no_menu to change item position.
113     # I added link in extensions.init_appbuilder_links.init_appbuilder_links
114     appbuilder.add_view_no_menu(views.RedocView)
115 
116 
117 def init_plugins(app):
118     """Integrate Flask and FAB with plugins"""
119     from airflow import plugins_manager
120 
121     plugins_manager.initialize_web_ui_plugins()
122 
123     appbuilder = app.appbuilder
124 
125     for view in plugins_manager.flask_appbuilder_views:
126         name = view.get('name')
127         if name:
128             log.debug("Adding view %s with menu", name)
129             appbuilder.add_view(view["view"], name, category=view["category"])
130         else:
131             # if 'name' key is missing, intent is to add view without menu
132             log.debug("Adding view %s without menu", str(type(view["view"])))
133             appbuilder.add_view_no_menu(view["view"])
134 
135     for menu_link in sorted(plugins_manager.flask_appbuilder_menu_links, key=lambda x: x["name"]):
136         log.debug("Adding menu link %s to %s", menu_link["name"], menu_link["href"])
137         appbuilder.add_link(**menu_link)
138 
139     for blue_print in plugins_manager.flask_blueprints:
140         log.debug("Adding blueprint %s:%s", blue_print["name"], blue_print["blueprint"].import_name)
141         app.register_blueprint(blue_print["blueprint"])
142 
143 
144 def init_connection_form():
145     """Initializes connection form"""
146     lazy_add_provider_discovered_options_to_connection_form()
147 
148 
149 def init_error_handlers(app: Flask):
150     """Add custom errors handlers"""
151     from airflow.www import views
152 
153     app.register_error_handler(500, views.show_traceback)
154     app.register_error_handler(404, views.not_found)
155 
156 
157 def set_cors_headers_on_response(response):
158     """Add response headers"""
159     allow_headers = conf.get('api', 'access_control_allow_headers')
160     allow_methods = conf.get('api', 'access_control_allow_methods')
161     allow_origins = conf.get('api', 'access_control_allow_origins')
162     if allow_headers is not None:
163         response.headers['Access-Control-Allow-Headers'] = allow_headers
164     if allow_methods is not None:
165         response.headers['Access-Control-Allow-Methods'] = allow_methods
166     if allow_origins is not None:
167         allowed_origins = allow_origins.split(' ')
168         origin = request.environ.get('HTTP_ORIGIN', allowed_origins[0])
169         if origin in allowed_origins:
170             response.headers['Access-Control-Allow-Origin'] = origin
171     return response
172 
173 
174 def init_api_connexion(app: Flask) -> None:
175     """Initialize Stable API"""
176     base_path = '/api/v1'
177 
178     from airflow.www import views
179 
180     @app.errorhandler(404)
181     @app.errorhandler(405)
182     def _handle_api_error(ex):
183         if request.path.startswith(base_path):
184             # 404 errors are never handled on the blueprint level
185             # unless raised from a view func so actual 404 errors,
186             # i.e. "no route for it" defined, need to be handled
187             # here on the application level
188             return common_error_handler(ex)
189         else:
190             return views.not_found(ex)
191 
192     spec_dir = path.join(ROOT_APP_DIR, 'api_connexion', 'openapi')
193     connexion_app = App(__name__, specification_dir=spec_dir, skip_error_handlers=True)
194     connexion_app.app = app
195     api_bp = connexion_app.add_api(
196         specification='v1.yaml', base_path=base_path, validate_responses=True, strict_validation=True
197     ).blueprint
198     # Like "api_bp.after_request", but the BP is already registered, so we have
199     # to register it in the app directly.
200     app.after_request_funcs.setdefault(api_bp.name, []).append(set_cors_headers_on_response)
201     app.register_error_handler(ProblemException, common_error_handler)
202     app.extensions['csrf'].exempt(api_bp)
203 
204 
205 def init_api_experimental(app):
206     """Initialize Experimental API"""
207     if not conf.getboolean('api', 'enable_experimental_api', fallback=False):
208         return
209     from airflow.www.api.experimental import endpoints
210 
211     warnings.warn(
212         "The experimental REST API is deprecated. Please migrate to the stable REST API. "
213         "Please note that the experimental API do not have access control. "
214         "The authenticated user has full access.",
215         DeprecationWarning,
216     )
217     app.register_blueprint(endpoints.api_experimental, url_prefix='/api/experimental')
218     app.extensions['csrf'].exempt(endpoints.api_experimental)
219
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/airflow/www/extensions/init_views.py b/airflow/www/extensions/init_views.py
--- a/airflow/www/extensions/init_views.py
+++ b/airflow/www/extensions/init_views.py
@@ -159,11 +159,13 @@
     allow_headers = conf.get('api', 'access_control_allow_headers')
     allow_methods = conf.get('api', 'access_control_allow_methods')
     allow_origins = conf.get('api', 'access_control_allow_origins')
-    if allow_headers is not None:
+    if allow_headers:
         response.headers['Access-Control-Allow-Headers'] = allow_headers
-    if allow_methods is not None:
+    if allow_methods:
         response.headers['Access-Control-Allow-Methods'] = allow_methods
-    if allow_origins is not None:
+    if allow_origins == '*':
+        response.headers['Access-Control-Allow-Origin'] = '*'
+    elif allow_origins:
         allowed_origins = allow_origins.split(' ')
         origin = request.environ.get('HTTP_ORIGIN', allowed_origins[0])
         if origin in allowed_origins:

{"golden_diff": "diff --git a/airflow/www/extensions/init_views.py b/airflow/www/extensions/init_views.py\n--- a/airflow/www/extensions/init_views.py\n+++ b/airflow/www/extensions/init_views.py\n@@ -159,11 +159,13 @@\n     allow_headers = conf.get('api', 'access_control_allow_headers')\n     allow_methods = conf.get('api', 'access_control_allow_methods')\n     allow_origins = conf.get('api', 'access_control_allow_origins')\n-    if allow_headers is not None:\n+    if allow_headers:\n         response.headers['Access-Control-Allow-Headers'] = allow_headers\n-    if allow_methods is not None:\n+    if allow_methods:\n         response.headers['Access-Control-Allow-Methods'] = allow_methods\n-    if allow_origins is not None:\n+    if allow_origins == '*':\n+        response.headers['Access-Control-Allow-Origin'] = '*'\n+    elif allow_origins:\n         allowed_origins = allow_origins.split(' ')\n         origin = request.environ.get('HTTP_ORIGIN', allowed_origins[0])\n         if origin in allowed_origins:\n", "issue": "CORS access_control_allow_origin header never returned\n### Apache Airflow version\n\n2.2.2 (latest released)\n\n### What happened\n\nTo fix CORS problem added the [access_control_allow_headers](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-headers), [access_control_allow_methods](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-methods), [access_control_allow_origins](https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#access-control-allow-origins) variables to the 2.2.2 docker-compose file provided in documentation. Both header, and methods returns with the correct value, but origins never does.\n\n### What you expected to happen\n\nThe CORS response returning with provided origin header value.\n\n### How to reproduce\n\nDownload the latest docker-compose from documentation add the following lines:\r\n`AIRFLOW__API__ACCESS_CONTROL_ALLOW_HEADERS: 'content-type, origin, authorization, accept'`\r\n`AIRFLOW__API__ACCESS_CONTROL_ALLOW_METHODS: 'GET, POST, OPTIONS, DELETE'`\r\n`AIRFLOW__API__ACCESS_CONTROL_ALLOW_ORIGINS: '*'`\r\n\r\nrun and call with a CORS preflight\n\n### Operating System\n\nWindows 11\n\n### Versions of Apache Airflow Providers\n\n_No response_\n\n### Deployment\n\nDocker-Compose\n\n### Deployment details\n\n_No response_\n\n### Anything else\n\nIt's repeatable regardless of ORIGINS value. There was a name change on this variable that's possibly not handled.\r\nOn 2.1.4 the same works without problems.\n\n### Are you willing to submit PR?\n\n- [ ] Yes I am willing to submit a PR!\n\n### Code of Conduct\n\n- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)\n\n", "before_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport logging\nimport warnings\nfrom os import path\n\nfrom connexion import App, ProblemException\nfrom flask import Flask, request\n\nfrom airflow.api_connexion.exceptions import common_error_handler\nfrom airflow.configuration import conf\nfrom airflow.security import permissions\nfrom airflow.www.views import lazy_add_provider_discovered_options_to_connection_form\n\nlog = logging.getLogger(__name__)\n\n# airflow/www/extensions/init_views.py => airflow/\nROOT_APP_DIR = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))\n\n\ndef init_flash_views(app):\n    \"\"\"Init main app view - redirect to FAB\"\"\"\n    from airflow.www.blueprints import routes\n\n    app.register_blueprint(routes)\n\n\ndef init_appbuilder_views(app):\n    \"\"\"Initialize Web UI views\"\"\"\n    appbuilder = app.appbuilder\n    from airflow.www import views\n\n    # Remove the session from scoped_session registry to avoid\n    # reusing a session with a disconnected connection\n    appbuilder.session.remove()\n    appbuilder.add_view_no_menu(views.AutocompleteView())\n    appbuilder.add_view_no_menu(views.Airflow())\n    appbuilder.add_view(\n        views.DagRunModelView,\n        permissions.RESOURCE_DAG_RUN,\n        category=permissions.RESOURCE_BROWSE_MENU,\n        category_icon=\"fa-globe\",\n    )\n    appbuilder.add_view(\n        views.JobModelView, permissions.RESOURCE_JOB, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.LogModelView, permissions.RESOURCE_AUDIT_LOG, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.VariableModelView, permissions.RESOURCE_VARIABLE, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.TaskInstanceModelView,\n        permissions.RESOURCE_TASK_INSTANCE,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.TaskRescheduleModelView,\n        permissions.RESOURCE_TASK_RESCHEDULE,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.TriggerModelView,\n        permissions.RESOURCE_TRIGGER,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.ConfigurationView,\n        permissions.RESOURCE_CONFIG,\n        category=permissions.RESOURCE_ADMIN_MENU,\n        category_icon=\"fa-user\",\n    )\n    appbuilder.add_view(\n        views.ConnectionModelView, permissions.RESOURCE_CONNECTION, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.SlaMissModelView, permissions.RESOURCE_SLA_MISS, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.PluginView, permissions.RESOURCE_PLUGIN, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.ProviderView, permissions.RESOURCE_PROVIDER, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.PoolModelView, permissions.RESOURCE_POOL, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.XComModelView, permissions.RESOURCE_XCOM, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.DagDependenciesView,\n        permissions.RESOURCE_DAG_DEPENDENCIES,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    # add_view_no_menu to change item position.\n    # I added link in extensions.init_appbuilder_links.init_appbuilder_links\n    appbuilder.add_view_no_menu(views.RedocView)\n\n\ndef init_plugins(app):\n    \"\"\"Integrate Flask and FAB with plugins\"\"\"\n    from airflow import plugins_manager\n\n    plugins_manager.initialize_web_ui_plugins()\n\n    appbuilder = app.appbuilder\n\n    for view in plugins_manager.flask_appbuilder_views:\n        name = view.get('name')\n        if name:\n            log.debug(\"Adding view %s with menu\", name)\n            appbuilder.add_view(view[\"view\"], name, category=view[\"category\"])\n        else:\n            # if 'name' key is missing, intent is to add view without menu\n            log.debug(\"Adding view %s without menu\", str(type(view[\"view\"])))\n            appbuilder.add_view_no_menu(view[\"view\"])\n\n    for menu_link in sorted(plugins_manager.flask_appbuilder_menu_links, key=lambda x: x[\"name\"]):\n        log.debug(\"Adding menu link %s to %s\", menu_link[\"name\"], menu_link[\"href\"])\n        appbuilder.add_link(**menu_link)\n\n    for blue_print in plugins_manager.flask_blueprints:\n        log.debug(\"Adding blueprint %s:%s\", blue_print[\"name\"], blue_print[\"blueprint\"].import_name)\n        app.register_blueprint(blue_print[\"blueprint\"])\n\n\ndef init_connection_form():\n    \"\"\"Initializes connection form\"\"\"\n    lazy_add_provider_discovered_options_to_connection_form()\n\n\ndef init_error_handlers(app: Flask):\n    \"\"\"Add custom errors handlers\"\"\"\n    from airflow.www import views\n\n    app.register_error_handler(500, views.show_traceback)\n    app.register_error_handler(404, views.not_found)\n\n\ndef set_cors_headers_on_response(response):\n    \"\"\"Add response headers\"\"\"\n    allow_headers = conf.get('api', 'access_control_allow_headers')\n    allow_methods = conf.get('api', 'access_control_allow_methods')\n    allow_origins = conf.get('api', 'access_control_allow_origins')\n    if allow_headers is not None:\n        response.headers['Access-Control-Allow-Headers'] = allow_headers\n    if allow_methods is not None:\n        response.headers['Access-Control-Allow-Methods'] = allow_methods\n    if allow_origins is not None:\n        allowed_origins = allow_origins.split(' ')\n        origin = request.environ.get('HTTP_ORIGIN', allowed_origins[0])\n        if origin in allowed_origins:\n            response.headers['Access-Control-Allow-Origin'] = origin\n    return response\n\n\ndef init_api_connexion(app: Flask) -> None:\n    \"\"\"Initialize Stable API\"\"\"\n    base_path = '/api/v1'\n\n    from airflow.www import views\n\n    @app.errorhandler(404)\n    @app.errorhandler(405)\n    def _handle_api_error(ex):\n        if request.path.startswith(base_path):\n            # 404 errors are never handled on the blueprint level\n            # unless raised from a view func so actual 404 errors,\n            # i.e. \"no route for it\" defined, need to be handled\n            # here on the application level\n            return common_error_handler(ex)\n        else:\n            return views.not_found(ex)\n\n    spec_dir = path.join(ROOT_APP_DIR, 'api_connexion', 'openapi')\n    connexion_app = App(__name__, specification_dir=spec_dir, skip_error_handlers=True)\n    connexion_app.app = app\n    api_bp = connexion_app.add_api(\n        specification='v1.yaml', base_path=base_path, validate_responses=True, strict_validation=True\n    ).blueprint\n    # Like \"api_bp.after_request\", but the BP is already registered, so we have\n    # to register it in the app directly.\n    app.after_request_funcs.setdefault(api_bp.name, []).append(set_cors_headers_on_response)\n    app.register_error_handler(ProblemException, common_error_handler)\n    app.extensions['csrf'].exempt(api_bp)\n\n\ndef init_api_experimental(app):\n    \"\"\"Initialize Experimental API\"\"\"\n    if not conf.getboolean('api', 'enable_experimental_api', fallback=False):\n        return\n    from airflow.www.api.experimental import endpoints\n\n    warnings.warn(\n        \"The experimental REST API is deprecated. Please migrate to the stable REST API. \"\n        \"Please note that the experimental API do not have access control. \"\n        \"The authenticated user has full access.\",\n        DeprecationWarning,\n    )\n    app.register_blueprint(endpoints.api_experimental, url_prefix='/api/experimental')\n    app.extensions['csrf'].exempt(endpoints.api_experimental)\n", "path": "airflow/www/extensions/init_views.py"}], "after_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport logging\nimport warnings\nfrom os import path\n\nfrom connexion import App, ProblemException\nfrom flask import Flask, request\n\nfrom airflow.api_connexion.exceptions import common_error_handler\nfrom airflow.configuration import conf\nfrom airflow.security import permissions\nfrom airflow.www.views import lazy_add_provider_discovered_options_to_connection_form\n\nlog = logging.getLogger(__name__)\n\n# airflow/www/extensions/init_views.py => airflow/\nROOT_APP_DIR = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))\n\n\ndef init_flash_views(app):\n    \"\"\"Init main app view - redirect to FAB\"\"\"\n    from airflow.www.blueprints import routes\n\n    app.register_blueprint(routes)\n\n\ndef init_appbuilder_views(app):\n    \"\"\"Initialize Web UI views\"\"\"\n    appbuilder = app.appbuilder\n    from airflow.www import views\n\n    # Remove the session from scoped_session registry to avoid\n    # reusing a session with a disconnected connection\n    appbuilder.session.remove()\n    appbuilder.add_view_no_menu(views.AutocompleteView())\n    appbuilder.add_view_no_menu(views.Airflow())\n    appbuilder.add_view(\n        views.DagRunModelView,\n        permissions.RESOURCE_DAG_RUN,\n        category=permissions.RESOURCE_BROWSE_MENU,\n        category_icon=\"fa-globe\",\n    )\n    appbuilder.add_view(\n        views.JobModelView, permissions.RESOURCE_JOB, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.LogModelView, permissions.RESOURCE_AUDIT_LOG, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.VariableModelView, permissions.RESOURCE_VARIABLE, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.TaskInstanceModelView,\n        permissions.RESOURCE_TASK_INSTANCE,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.TaskRescheduleModelView,\n        permissions.RESOURCE_TASK_RESCHEDULE,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.TriggerModelView,\n        permissions.RESOURCE_TRIGGER,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    appbuilder.add_view(\n        views.ConfigurationView,\n        permissions.RESOURCE_CONFIG,\n        category=permissions.RESOURCE_ADMIN_MENU,\n        category_icon=\"fa-user\",\n    )\n    appbuilder.add_view(\n        views.ConnectionModelView, permissions.RESOURCE_CONNECTION, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.SlaMissModelView, permissions.RESOURCE_SLA_MISS, category=permissions.RESOURCE_BROWSE_MENU\n    )\n    appbuilder.add_view(\n        views.PluginView, permissions.RESOURCE_PLUGIN, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.ProviderView, permissions.RESOURCE_PROVIDER, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.PoolModelView, permissions.RESOURCE_POOL, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.XComModelView, permissions.RESOURCE_XCOM, category=permissions.RESOURCE_ADMIN_MENU\n    )\n    appbuilder.add_view(\n        views.DagDependenciesView,\n        permissions.RESOURCE_DAG_DEPENDENCIES,\n        category=permissions.RESOURCE_BROWSE_MENU,\n    )\n    # add_view_no_menu to change item position.\n    # I added link in extensions.init_appbuilder_links.init_appbuilder_links\n    appbuilder.add_view_no_menu(views.RedocView)\n\n\ndef init_plugins(app):\n    \"\"\"Integrate Flask and FAB with plugins\"\"\"\n    from airflow import plugins_manager\n\n    plugins_manager.initialize_web_ui_plugins()\n\n    appbuilder = app.appbuilder\n\n    for view in plugins_manager.flask_appbuilder_views:\n        name = view.get('name')\n        if name:\n            log.debug(\"Adding view %s with menu\", name)\n            appbuilder.add_view(view[\"view\"], name, category=view[\"category\"])\n        else:\n            # if 'name' key is missing, intent is to add view without menu\n            log.debug(\"Adding view %s without menu\", str(type(view[\"view\"])))\n            appbuilder.add_view_no_menu(view[\"view\"])\n\n    for menu_link in sorted(plugins_manager.flask_appbuilder_menu_links, key=lambda x: x[\"name\"]):\n        log.debug(\"Adding menu link %s to %s\", menu_link[\"name\"], menu_link[\"href\"])\n        appbuilder.add_link(**menu_link)\n\n    for blue_print in plugins_manager.flask_blueprints:\n        log.debug(\"Adding blueprint %s:%s\", blue_print[\"name\"], blue_print[\"blueprint\"].import_name)\n        app.register_blueprint(blue_print[\"blueprint\"])\n\n\ndef init_connection_form():\n    \"\"\"Initializes connection form\"\"\"\n    lazy_add_provider_discovered_options_to_connection_form()\n\n\ndef init_error_handlers(app: Flask):\n    \"\"\"Add custom errors handlers\"\"\"\n    from airflow.www import views\n\n    app.register_error_handler(500, views.show_traceback)\n    app.register_error_handler(404, views.not_found)\n\n\ndef set_cors_headers_on_response(response):\n    \"\"\"Add response headers\"\"\"\n    allow_headers = conf.get('api', 'access_control_allow_headers')\n    allow_methods = conf.get('api', 'access_control_allow_methods')\n    allow_origins = conf.get('api', 'access_control_allow_origins')\n    if allow_headers:\n        response.headers['Access-Control-Allow-Headers'] = allow_headers\n    if allow_methods:\n        response.headers['Access-Control-Allow-Methods'] = allow_methods\n    if allow_origins == '*':\n        response.headers['Access-Control-Allow-Origin'] = '*'\n    elif allow_origins:\n        allowed_origins = allow_origins.split(' ')\n        origin = request.environ.get('HTTP_ORIGIN', allowed_origins[0])\n        if origin in allowed_origins:\n            response.headers['Access-Control-Allow-Origin'] = origin\n    return response\n\n\ndef init_api_connexion(app: Flask) -> None:\n    \"\"\"Initialize Stable API\"\"\"\n    base_path = '/api/v1'\n\n    from airflow.www import views\n\n    @app.errorhandler(404)\n    @app.errorhandler(405)\n    def _handle_api_error(ex):\n        if request.path.startswith(base_path):\n            # 404 errors are never handled on the blueprint level\n            # unless raised from a view func so actual 404 errors,\n            # i.e. \"no route for it\" defined, need to be handled\n            # here on the application level\n            return common_error_handler(ex)\n        else:\n            return views.not_found(ex)\n\n    spec_dir = path.join(ROOT_APP_DIR, 'api_connexion', 'openapi')\n    connexion_app = App(__name__, specification_dir=spec_dir, skip_error_handlers=True)\n    connexion_app.app = app\n    api_bp = connexion_app.add_api(\n        specification='v1.yaml', base_path=base_path, validate_responses=True, strict_validation=True\n    ).blueprint\n    # Like \"api_bp.after_request\", but the BP is already registered, so we have\n    # to register it in the app directly.\n    app.after_request_funcs.setdefault(api_bp.name, []).append(set_cors_headers_on_response)\n    app.register_error_handler(ProblemException, common_error_handler)\n    app.extensions['csrf'].exempt(api_bp)\n\n\ndef init_api_experimental(app):\n    \"\"\"Initialize Experimental API\"\"\"\n    if not conf.getboolean('api', 'enable_experimental_api', fallback=False):\n        return\n    from airflow.www.api.experimental import endpoints\n\n    warnings.warn(\n        \"The experimental REST API is deprecated. Please migrate to the stable REST API. \"\n        \"Please note that the experimental API do not have access control. \"\n        \"The authenticated user has full access.\",\n        DeprecationWarning,\n    )\n    app.register_blueprint(endpoints.api_experimental, url_prefix='/api/experimental')\n    app.extensions['csrf'].exempt(endpoints.api_experimental)\n", "path": "airflow/www/extensions/init_views.py"}]}