|
| 1 | +import time |
| 2 | +from importlib import import_module |
| 3 | + |
| 4 | +from django.conf import settings |
| 5 | +from django.contrib.sessions.backends.base import UpdateError |
| 6 | +from django.contrib.sessions.middleware import SessionMiddleware |
| 7 | +from django.core.exceptions import SuspiciousOperation |
| 8 | +from django.utils.cache import patch_vary_headers |
| 9 | +from django.utils.http import http_date |
| 10 | + |
| 11 | + |
| 12 | +class SamlSessionMiddleware(SessionMiddleware): |
| 13 | + session_name = getattr(settings, 'SAML_SESSION_COOKIE_NAME', 'saml_session') |
| 14 | + |
| 15 | + def process_request(self, request): |
| 16 | + session_key = request.COOKIES.get(self.session_name, None) |
| 17 | + setattr(request, self.session_name, self.SessionStore(session_key)) |
| 18 | + |
| 19 | + def process_response(self, request, response): |
| 20 | + """ |
| 21 | + If request.saml_session was modified, or if the configuration is to save the |
| 22 | + session every time, save the changes and set a session cookie or delete |
| 23 | + the session cookie if the session has been emptied. |
| 24 | + """ |
| 25 | + try: |
| 26 | + accessed = getattr(request, self.session_name).accessed |
| 27 | + modified = getattr(request, self.session_name).modified |
| 28 | + empty = getattr(request, self.session_name).is_empty() |
| 29 | + except AttributeError: |
| 30 | + return response |
| 31 | + # First check if we need to delete this cookie. |
| 32 | + # The session should be deleted only if the session is entirely empty. |
| 33 | + if self.session_name in request.COOKIES and empty: |
| 34 | + response.delete_cookie( |
| 35 | + self.session_name, |
| 36 | + path=settings.SESSION_COOKIE_PATH, |
| 37 | + domain=settings.SESSION_COOKIE_DOMAIN, |
| 38 | + samesite=None, |
| 39 | + ) |
| 40 | + patch_vary_headers(response, ('Cookie',)) |
| 41 | + else: |
| 42 | + if accessed: |
| 43 | + patch_vary_headers(response, ('Cookie',)) |
| 44 | + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: |
| 45 | + if request.session.get_expire_at_browser_close(): |
| 46 | + max_age = None |
| 47 | + expires = None |
| 48 | + else: |
| 49 | + max_age = getattr(request, self.session_name).get_expiry_age() |
| 50 | + expires_time = time.time() + max_age |
| 51 | + expires = http_date(expires_time) |
| 52 | + # Save the session data and refresh the client cookie. |
| 53 | + # Skip session save for 500 responses, refs #3881. |
| 54 | + if response.status_code != 500: |
| 55 | + try: |
| 56 | + getattr(request, self.session_name).save() |
| 57 | + except UpdateError: |
| 58 | + raise SuspiciousOperation( |
| 59 | + "The request's session was deleted before the " |
| 60 | + "request completed. The user may have logged " |
| 61 | + "out in a concurrent request, for example." |
| 62 | + ) |
| 63 | + response.set_cookie( |
| 64 | + self.session_name, |
| 65 | + getattr(request, self.session_name).session_key, |
| 66 | + max_age=max_age, |
| 67 | + expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, |
| 68 | + path=settings.SESSION_COOKIE_PATH, |
| 69 | + secure=settings.SESSION_COOKIE_SECURE or None, |
| 70 | + httponly=settings.SESSION_COOKIE_HTTPONLY or None, |
| 71 | + samesite=None |
| 72 | + ) |
| 73 | + return response |
0 commit comments