mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 17:51:08 -05:00
okay fine
This commit is contained in:
268
.venv/lib/python3.12/site-packages/channels/sessions.py
Normal file
268
.venv/lib/python3.12/site-packages/channels/sessions.py
Normal file
@@ -0,0 +1,268 @@
|
||||
import datetime
|
||||
import time
|
||||
from importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import UpdateError
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.http import parse_cookie
|
||||
from django.http.cookie import SimpleCookie
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.functional import LazyObject
|
||||
|
||||
from channels.db import database_sync_to_async
|
||||
|
||||
try:
|
||||
from django.utils.http import http_date
|
||||
except ImportError:
|
||||
from django.utils.http import cookie_date as http_date
|
||||
|
||||
|
||||
class CookieMiddleware:
|
||||
"""
|
||||
Extracts cookies from HTTP or WebSocket-style scopes and adds them as a
|
||||
scope["cookies"] entry with the same format as Django's request.COOKIES.
|
||||
"""
|
||||
|
||||
def __init__(self, inner):
|
||||
self.inner = inner
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
# Check this actually has headers. They're a required scope key for HTTP and WS.
|
||||
if "headers" not in scope:
|
||||
raise ValueError(
|
||||
"CookieMiddleware was passed a scope that did not have a headers key "
|
||||
+ "(make sure it is only passed HTTP or WebSocket connections)"
|
||||
)
|
||||
# Go through headers to find the cookie one
|
||||
for name, value in scope.get("headers", []):
|
||||
if name == b"cookie":
|
||||
cookies = parse_cookie(value.decode("latin1"))
|
||||
break
|
||||
else:
|
||||
# No cookie header found - add an empty default.
|
||||
cookies = {}
|
||||
# Return inner application
|
||||
return await self.inner(dict(scope, cookies=cookies), receive, send)
|
||||
|
||||
@classmethod
|
||||
def set_cookie(
|
||||
cls,
|
||||
message,
|
||||
key,
|
||||
value="",
|
||||
max_age=None,
|
||||
expires=None,
|
||||
path="/",
|
||||
domain=None,
|
||||
secure=False,
|
||||
httponly=False,
|
||||
samesite="lax",
|
||||
):
|
||||
"""
|
||||
Sets a cookie in the passed HTTP response message.
|
||||
|
||||
``expires`` can be:
|
||||
- a string in the correct format,
|
||||
- a naive ``datetime.datetime`` object in UTC,
|
||||
- an aware ``datetime.datetime`` object in any time zone.
|
||||
If it is a ``datetime.datetime`` object then ``max_age`` will be calculated.
|
||||
"""
|
||||
value = force_str(value)
|
||||
cookies = SimpleCookie()
|
||||
cookies[key] = value
|
||||
if expires is not None:
|
||||
if isinstance(expires, datetime.datetime):
|
||||
if timezone.is_aware(expires):
|
||||
expires = timezone.make_naive(expires, timezone.utc)
|
||||
delta = expires - expires.utcnow()
|
||||
# Add one second so the date matches exactly (a fraction of
|
||||
# time gets lost between converting to a timedelta and
|
||||
# then the date string).
|
||||
delta = delta + datetime.timedelta(seconds=1)
|
||||
# Just set max_age - the max_age logic will set expires.
|
||||
expires = None
|
||||
max_age = max(0, delta.days * 86400 + delta.seconds)
|
||||
else:
|
||||
cookies[key]["expires"] = expires
|
||||
else:
|
||||
cookies[key]["expires"] = ""
|
||||
if max_age is not None:
|
||||
cookies[key]["max-age"] = max_age
|
||||
# IE requires expires, so set it if hasn't been already.
|
||||
if not expires:
|
||||
cookies[key]["expires"] = http_date(time.time() + max_age)
|
||||
if path is not None:
|
||||
cookies[key]["path"] = path
|
||||
if domain is not None:
|
||||
cookies[key]["domain"] = domain
|
||||
if secure:
|
||||
cookies[key]["secure"] = True
|
||||
if httponly:
|
||||
cookies[key]["httponly"] = True
|
||||
if samesite is not None:
|
||||
assert samesite.lower() in [
|
||||
"strict",
|
||||
"lax",
|
||||
"none",
|
||||
], "samesite must be either 'strict', 'lax' or 'none'"
|
||||
cookies[key]["samesite"] = samesite
|
||||
# Write out the cookies to the response
|
||||
for c in cookies.values():
|
||||
message.setdefault("headers", []).append(
|
||||
(b"Set-Cookie", bytes(c.output(header=""), encoding="utf-8"))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def delete_cookie(cls, message, key, path="/", domain=None):
|
||||
"""
|
||||
Deletes a cookie in a response.
|
||||
"""
|
||||
return cls.set_cookie(
|
||||
message,
|
||||
key,
|
||||
max_age=0,
|
||||
path=path,
|
||||
domain=domain,
|
||||
expires="Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
)
|
||||
|
||||
|
||||
class InstanceSessionWrapper:
|
||||
"""
|
||||
Populates the session in application instance scope, and wraps send to save
|
||||
the session.
|
||||
"""
|
||||
|
||||
# Message types that trigger a session save if it's modified
|
||||
save_message_types = ["http.response.start"]
|
||||
|
||||
# Message types that can carry session cookies back
|
||||
cookie_response_message_types = ["http.response.start"]
|
||||
|
||||
def __init__(self, scope, send):
|
||||
self.cookie_name = settings.SESSION_COOKIE_NAME
|
||||
self.session_store = import_module(settings.SESSION_ENGINE).SessionStore
|
||||
|
||||
self.scope = dict(scope)
|
||||
|
||||
if "session" in self.scope:
|
||||
# There's already session middleware of some kind above us, pass
|
||||
# that through
|
||||
self.activated = False
|
||||
else:
|
||||
# Make sure there are cookies in the scope
|
||||
if "cookies" not in self.scope:
|
||||
raise ValueError(
|
||||
"No cookies in scope - SessionMiddleware needs to run "
|
||||
"inside of CookieMiddleware."
|
||||
)
|
||||
# Parse the headers in the scope into cookies
|
||||
self.scope["session"] = LazyObject()
|
||||
self.activated = True
|
||||
|
||||
# Override send
|
||||
self.real_send = send
|
||||
|
||||
async def resolve_session(self):
|
||||
session_key = self.scope["cookies"].get(self.cookie_name)
|
||||
self.scope["session"]._wrapped = await database_sync_to_async(
|
||||
self.session_store
|
||||
)(session_key)
|
||||
|
||||
async def send(self, message):
|
||||
"""
|
||||
Overridden send that also does session saves/cookies.
|
||||
"""
|
||||
# Only save session if we're the outermost session middleware
|
||||
if self.activated:
|
||||
modified = self.scope["session"].modified
|
||||
empty = self.scope["session"].is_empty()
|
||||
# If this is a message type that we want to save on, and there's
|
||||
# changed data, save it. We also save if it's empty as we might
|
||||
# not be able to send a cookie-delete along with this message.
|
||||
if (
|
||||
message["type"] in self.save_message_types
|
||||
and message.get("status", 200) != 500
|
||||
and (modified or settings.SESSION_SAVE_EVERY_REQUEST)
|
||||
):
|
||||
await database_sync_to_async(self.save_session)()
|
||||
# If this is a message type that can transport cookies back to the
|
||||
# client, then do so.
|
||||
if message["type"] in self.cookie_response_message_types:
|
||||
if empty:
|
||||
# Delete cookie if it's set
|
||||
if settings.SESSION_COOKIE_NAME in self.scope["cookies"]:
|
||||
CookieMiddleware.delete_cookie(
|
||||
message,
|
||||
settings.SESSION_COOKIE_NAME,
|
||||
path=settings.SESSION_COOKIE_PATH,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
)
|
||||
else:
|
||||
# Get the expiry data
|
||||
if self.scope["session"].get_expire_at_browser_close():
|
||||
max_age = None
|
||||
expires = None
|
||||
else:
|
||||
max_age = self.scope["session"].get_expiry_age()
|
||||
expires_time = time.time() + max_age
|
||||
expires = http_date(expires_time)
|
||||
# Set the cookie
|
||||
CookieMiddleware.set_cookie(
|
||||
message,
|
||||
self.cookie_name,
|
||||
self.scope["session"].session_key,
|
||||
max_age=max_age,
|
||||
expires=expires,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
path=settings.SESSION_COOKIE_PATH,
|
||||
secure=settings.SESSION_COOKIE_SECURE or None,
|
||||
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
||||
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||
)
|
||||
# Pass up the send
|
||||
return await self.real_send(message)
|
||||
|
||||
def save_session(self):
|
||||
"""
|
||||
Saves the current session.
|
||||
"""
|
||||
try:
|
||||
self.scope["session"].save()
|
||||
except UpdateError:
|
||||
raise SuspiciousOperation(
|
||||
"The request's session was deleted before the "
|
||||
"request completed. The user may have logged "
|
||||
"out in a concurrent request, for example."
|
||||
)
|
||||
|
||||
|
||||
class SessionMiddleware:
|
||||
"""
|
||||
Class that adds Django sessions (from HTTP cookies) to the
|
||||
scope. Works with HTTP or WebSocket protocol types (or anything that
|
||||
provides a "headers" entry in the scope).
|
||||
|
||||
Requires the CookieMiddleware to be higher up in the stack.
|
||||
"""
|
||||
|
||||
def __init__(self, inner):
|
||||
self.inner = inner
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
"""
|
||||
Instantiate a session wrapper for this scope, resolve the session and
|
||||
call the inner application.
|
||||
"""
|
||||
wrapper = InstanceSessionWrapper(scope, send)
|
||||
|
||||
await wrapper.resolve_session()
|
||||
|
||||
return await self.inner(wrapper.scope, receive, wrapper.send)
|
||||
|
||||
|
||||
# Shortcut to include cookie middleware
|
||||
def SessionMiddlewareStack(inner):
|
||||
return CookieMiddleware(SessionMiddleware(inner))
|
||||
Reference in New Issue
Block a user