Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions airflow/providers/fab/auth_manager/security_manager/override.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@
AuthOAuthView,
AuthOIDView,
AuthRemoteUserView,
AuthView,
RegisterUserModelView,
)
from flask_appbuilder.views import expose
from flask_babel import lazy_gettext
from flask_jwt_extended import JWTManager, current_user as current_user_jwt
from flask_login import LoginManager
Expand Down Expand Up @@ -123,6 +125,21 @@
MAX_NUM_DATABASE_USER_SESSIONS = 50000


# The following class patches the logout method within AuthView, so it only supports POST method
# to make CSRF protection effective. You could remove the patch and configure it when it is supported
# natively by Flask-AppBuilder (https://github.com/dpgaspar/Flask-AppBuilder/issues/2248)


class _ModifiedAuthView(AuthView):
@expose("/logout/", methods=["POST"])
def logout(self):
return super().logout()


for auth_view in [AuthDBView, AuthLDAPView, AuthOAuthView, AuthOIDView, AuthRemoteUserView]:
auth_view.__bases__ = (_ModifiedAuthView,)


class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
"""
This security manager overrides the default AirflowSecurityManager security manager.
Expand Down
18 changes: 15 additions & 3 deletions airflow/www/static/css/bootstrap-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -2768,17 +2768,25 @@ tbody.collapse.in {
overflow: hidden;
background-color: #e5e5e5;
}
.dropdown-menu > li > a {

.dropdown-menu > li > a,
.dropdown-menu > li > form > button
{
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
color: #51504f;
white-space: nowrap;
width: 100%;
max-width: 100%;
text-align: left;
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
.dropdown-menu > li > a:focus,
.dropdown-menu > li > form > button:hover,
.dropdown-menu > li > form > button:focus {
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
Expand Down Expand Up @@ -3569,13 +3577,17 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
box-shadow: none;
}
.navbar-nav .dropdown:hover > .dropdown-menu > li > a,
.navbar-nav .dropdown:hover > .dropdown-menu > li > form > button,
.navbar-nav .dropdown:hover > .dropdown-menu .dropdown-header,
.navbar-nav .dropdown:focus-within > .dropdown-menu > li > a,
.navbar-nav .dropdown:focus-within > .dropdown-menu > li > form > button,
.navbar-nav .dropdown:focus-within > .dropdown-menu .dropdown-header {
padding: 5px 15px 5px 25px;
}
.navbar-nav .dropdown:hover > .dropdown-menu > li > a,
.navbar-nav .dropdown:focus-within > .dropdown-menu > li > a {
.navbar-nav .dropdown:hover > .dropdown-menu > li > form > button,
.navbar-nav .dropdown:focus-within > .dropdown-menu > li > a,
.navbar-nav .dropdown:focus-within > .dropdown-menu > li > form > button {
line-height: 20px;
}
.navbar-nav .dropdown:hover > .dropdown-menu > li > a:hover,
Expand Down
4 changes: 3 additions & 1 deletion airflow/www/static/css/material-icons.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion airflow/www/templates/appbuilder/navbar_right.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@
<li><a href="{{user_profile_url}}"><span class="material-icons">account_circle</span>{{_("Your Profile")}}</a></li>
<li role="separator" class="divider"></li>
{% endif %}
<li><a href="{{auth_manager.get_url_logout()}}"><span class="material-icons">exit_to_app</span>{{_("Log Out")}}</a></li>
<li>
<form method="POST" action="{{auth_manager.get_url_logout()}}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-link"><span class="material-icons">exit_to_app</span>{{_("Log Out")}}</button>
</form>
</li>
</ul>
</li>
{% else %}
Expand Down
5 changes: 5 additions & 0 deletions newsfragments/40145.significant.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``/logout`` endpoint in FAB Auth Manager is now CSRF protected

The ``/logout`` endpoint's method in FAB Auth Manager has been changed from ``GET`` to ``POST`` in all existing
AuthViews (``AuthDBView``, ``AuthLDAPView``, ``AuthOAuthView``, ``AuthOIDView``, ``AuthRemoteUserView``), and
now includes CSRF protection to enhance security and prevent unauthorized logouts.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
delete_user,
)

pytestmark = pytest.mark.db_test


@pytest.fixture(scope="module")
def configured_app(minimal_app_for_auth_api):
Expand Down
4 changes: 2 additions & 2 deletions tests/www/views/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_session_inaccessible_after_logout(user_client):
session_cookie = get_session_cookie(user_client)
assert session_cookie is not None

resp = user_client.get("/logout/")
resp = user_client.post("/logout/")
assert resp.status_code == 302

# Try to access /home with the session cookie from earlier
Expand Down Expand Up @@ -78,7 +78,7 @@ def test_session_id_rotates(app, user_client):
old_session_cookie = get_session_cookie(user_client)
assert old_session_cookie is not None

resp = user_client.get("/logout/")
resp = user_client.post("/logout/")
assert resp.status_code == 302

patch_path = "airflow.providers.fab.auth_manager.security_manager.override.check_password_hash"
Expand Down