Skip to content

Commit 3030e88

Browse files
authored
feat: add no cache directive to login forms (#2266)
* feat: add no cache directive to login forms * add test
1 parent 4ddea80 commit 3030e88

File tree

3 files changed

+31
-1
lines changed

3 files changed

+31
-1
lines changed

flask_appbuilder/security/decorators.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@
3030
P = ParamSpec("P")
3131

3232

33+
def no_cache(view: Callable[..., Response]) -> Callable[..., Response]:
34+
@functools.wraps(view)
35+
def wrapped_view(*args, **kwargs) -> Response:
36+
response = make_response(view(*args, **kwargs))
37+
response.headers[
38+
"Cache-Control"
39+
] = "no-store, no-cache, must-revalidate, max-age=0"
40+
response.headers["Pragma"] = "no-cache"
41+
response.headers["Expires"] = "0"
42+
return response
43+
44+
return wrapped_view
45+
46+
3347
def response_unauthorized_mvc(status_code: int) -> Response:
3448
response = make_response(
3549
jsonify({"message": str(FLAMSG_ERR_SEC_ACCESS_DENIED), "severity": "danger"}),

flask_appbuilder/security/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from flask_appbuilder.baseviews import BaseView
1010
from flask_appbuilder.charts.views import DirectByChartView
1111
from flask_appbuilder.fieldwidgets import BS3PasswordFieldWidget
12-
from flask_appbuilder.security.decorators import has_access
12+
from flask_appbuilder.security.decorators import has_access, no_cache
1313
from flask_appbuilder.security.forms import (
1414
DynamicForm,
1515
LoginForm_db,
@@ -520,6 +520,7 @@ class AuthDBView(AuthView):
520520
login_template = "appbuilder/general/security/login_db.html"
521521

522522
@expose("/login/", methods=["GET", "POST"])
523+
@no_cache
523524
def login(self):
524525
if g.user is not None and g.user.is_authenticated:
525526
return redirect(self.appbuilder.get_url_for_index)
@@ -543,6 +544,7 @@ class AuthLDAPView(AuthView):
543544
login_template = "appbuilder/general/security/login_ldap.html"
544545

545546
@expose("/login/", methods=["GET", "POST"])
547+
@no_cache
546548
def login(self):
547549
if g.user is not None and g.user.is_authenticated:
548550
return redirect(self.appbuilder.get_url_for_index)
@@ -568,6 +570,7 @@ class AuthOIDView(AuthView):
568570
oid_ask_for_optional: List[str] = []
569571

570572
@expose("/login/", methods=["GET", "POST"])
573+
@no_cache
571574
def login(self, flag=True) -> WerkzeugResponse:
572575
@self.appbuilder.sm.oid.loginhandler
573576
def login_handler(self):

tests/security/test_mvc_security.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ class Model1View(ModelView):
6363

6464
self.appbuilder.add_view(Model1View, "Model1", category="Model1")
6565

66+
def test_sec_login_no_cache(self):
67+
"""
68+
Test Security Login, no cache directives
69+
"""
70+
rv = self.client.get("/login/")
71+
assert rv.status_code == 200
72+
assert (
73+
rv.headers.get("Cache-Control")
74+
== "no-store, no-cache, must-revalidate, max-age=0"
75+
)
76+
assert rv.headers["Pragma"] == "no-cache"
77+
assert rv.headers["Expires"] == "0"
78+
6679
def test_sec_login(self):
6780
"""
6881
Test Security Login, Logout, invalid login, invalid access

0 commit comments

Comments
 (0)