1
- import time
2
-
3
1
import structlog
4
2
from django .conf import settings
5
- from django .contrib .sessions .backends .base import SessionBase , UpdateError
3
+ from django .contrib .sessions .backends .base import SessionBase
6
4
from django .contrib .sessions .middleware import SessionMiddleware
7
- from django .core .exceptions import (
8
- ImproperlyConfigured ,
9
- MiddlewareNotUsed ,
10
- SuspiciousOperation ,
11
- )
5
+ from django .core .exceptions import ImproperlyConfigured , MiddlewareNotUsed
12
6
from django .http import HttpResponse
13
- from django .utils .cache import patch_vary_headers
14
- from django .utils .http import http_date
15
7
16
8
log = structlog .get_logger (__name__ )
17
9
@@ -22,17 +14,6 @@ class ReadTheDocsSessionMiddleware(SessionMiddleware):
22
14
An overridden session middleware with a few changes.
23
15
24
16
- Doesn't create a session on logged out doc views.
25
- - Uses a fallback cookie for browsers that reject SameSite=None cookies
26
- - Modifies Django's behavior of treating SESSION_COOKIE_SAMESITE=None
27
- to mean SameSite unset and instead makes it mean SameSite=None
28
-
29
- This overrides and replaces Django's built-in SessionMiddleware completely.
30
- Much of this middleware is duplicated from Django 2.2's SessionMiddleware.
31
-
32
- In Django 3.1, Django will fully support SameSite=None cookies.
33
- However, we may still need this middleware to support browsers that reject
34
- SameSite=None cookies.
35
- https://www.chromium.org/updates/same-site/incompatible-clients
36
17
"""
37
18
38
19
# Don't set a session cookie on these URLs unless the cookie is already set
@@ -42,125 +23,27 @@ class ReadTheDocsSessionMiddleware(SessionMiddleware):
42
23
"/sustainability/click" ,
43
24
]
44
25
45
- # This is a fallback cookie for the regular session cookie
46
- # It is only used by clients that reject cookies with `SameSite=None`
47
- cookie_name_fallback = f"{ settings .SESSION_COOKIE_NAME } -samesiteunset"
48
-
49
26
def process_request (self , request ):
50
27
for url in self .IGNORE_URLS :
51
28
if (
52
29
request .path_info .startswith (url )
53
30
and settings .SESSION_COOKIE_NAME not in request .COOKIES
54
- and self .cookie_name_fallback not in request .COOKIES
55
31
):
56
32
# Hack request.session otherwise the Authentication middleware complains.
57
33
request .session = SessionBase () # create an empty session
58
34
return
59
35
60
- if settings .SESSION_COOKIE_SAMESITE == "None" :
61
- if settings .SESSION_COOKIE_NAME in request .COOKIES :
62
- session_key = request .COOKIES .get (settings .SESSION_COOKIE_NAME )
63
- else :
64
- session_key = request .COOKIES .get (self .cookie_name_fallback )
65
-
66
- request .session = self .SessionStore (session_key )
67
- else :
68
- super ().process_request (request )
36
+ super ().process_request (request )
69
37
70
38
def process_response (self , request , response ):
71
39
for url in self .IGNORE_URLS :
72
40
if (
73
41
request .path_info .startswith (url )
74
42
and settings .SESSION_COOKIE_NAME not in request .COOKIES
75
- and self .cookie_name_fallback not in request .COOKIES
76
43
):
77
44
return response
78
45
79
- # Most of the code below is taken directly from Django's SessionMiddleware.
80
- # Some changes (marked with NOTE:) were added to support the fallback cookie.
81
-
82
- try :
83
- accessed = request .session .accessed
84
- modified = request .session .modified
85
- empty = request .session .is_empty ()
86
- except AttributeError :
87
- pass
88
- else :
89
- # First check if we need to delete this cookie.
90
- # The session should be deleted only if the session is entirely empty
91
- # NOTE: This was changed to support both cookies
92
- if (
93
- settings .SESSION_COOKIE_NAME in request .COOKIES
94
- or self .cookie_name_fallback in request .COOKIES
95
- ) and empty :
96
- for cookie_name in (
97
- settings .SESSION_COOKIE_NAME ,
98
- self .cookie_name_fallback ,
99
- ):
100
- if cookie_name in request .COOKIES :
101
- response .delete_cookie (
102
- cookie_name ,
103
- path = settings .SESSION_COOKIE_PATH ,
104
- domain = settings .SESSION_COOKIE_DOMAIN ,
105
- )
106
- else :
107
- if accessed :
108
- patch_vary_headers (response , ("Cookie" ,))
109
- if (modified or settings .SESSION_SAVE_EVERY_REQUEST ) and not empty :
110
- if request .session .get_expire_at_browser_close ():
111
- max_age = None
112
- expires = None
113
- else :
114
- max_age = request .session .get_expiry_age ()
115
- expires_time = time .time () + max_age
116
- expires = http_date (expires_time )
117
- # Save the session data and refresh the client cookie.
118
- # Skip session save for 500 responses, refs #3881.
119
- if response .status_code != 500 :
120
- try :
121
- request .session .save ()
122
- except UpdateError :
123
- raise SuspiciousOperation (
124
- "The request's session was deleted before the "
125
- "request completed. The user may have logged "
126
- "out in a concurrent request, for example."
127
- )
128
-
129
- response .set_cookie (
130
- settings .SESSION_COOKIE_NAME ,
131
- request .session .session_key ,
132
- max_age = max_age ,
133
- expires = expires ,
134
- domain = settings .SESSION_COOKIE_DOMAIN ,
135
- path = settings .SESSION_COOKIE_PATH ,
136
- secure = settings .SESSION_COOKIE_SECURE or None ,
137
- httponly = settings .SESSION_COOKIE_HTTPONLY or None ,
138
- samesite = settings .SESSION_COOKIE_SAMESITE ,
139
- )
140
-
141
- # NOTE: This was added to support the fallback cookie
142
- if settings .SESSION_COOKIE_SAMESITE == "None" :
143
- # Forcibly set the session cookie to SameSite=None
144
- # This isn't supported in Django<3.1
145
- # https://github.com/django/django/pull/11894
146
- response .cookies [settings .SESSION_COOKIE_NAME ][
147
- "samesite"
148
- ] = "None"
149
-
150
- # Set the fallback cookie in case the above cookie is rejected
151
- response .set_cookie (
152
- self .cookie_name_fallback ,
153
- request .session .session_key ,
154
- max_age = max_age ,
155
- expires = expires ,
156
- domain = settings .SESSION_COOKIE_DOMAIN ,
157
- path = settings .SESSION_COOKIE_PATH ,
158
- secure = settings .SESSION_COOKIE_SECURE or None ,
159
- httponly = settings .SESSION_COOKIE_HTTPONLY or None ,
160
- # Use browser default in case SameSite=None is rejected.
161
- samesite = False ,
162
- )
163
- return response
46
+ return super ().process_response (request , response )
164
47
165
48
166
49
class ReferrerPolicyMiddleware :
0 commit comments