def __call__(self, scope: Scope) -> NoReturn: # Get the path path = scope.get("path_remaining", scope.get("path", None)) if path is None: raise ValueError( "No 'path' key in connection scope, cannot route URLs") # Remove leading / to match Django's handling path = path.lstrip("/") # Run through the routes we have until one matches for route in self.routes: try: match = route_pattern_match(route, path) if match: new_path, args, kwargs = match # Add args or kwargs into the scope outer = scope.get("url_route", {}) return route.callback( dict( scope, path_remaining=new_path, url_route={ "args": outer.get("args", ()) + args, "kwargs": { **outer.get("kwargs", {}), **kwargs }, }, )) except Resolver404: pass else: if "path_remaining" in scope: raise Resolver404("No route found for path %r." % path) # We are the outermost URLRouter raise ValueError("No route found for path %r." % path)
def set_cookie( cls, message: Scope, key: str, value: str = "", max_age: Optional[int] = None, expires: Optional[Union[datetime, str]] = None, path: str = "/", domain: Optional[str] = None, secure: bool = False, httponly: bool = False, ) -> NoReturn: """ 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): 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 + 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 # 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")))
def get_application(self, options: Scope): """ Returns the static files serving application wrapping the default application, if static files should be served. Otherwise just returns the default handler. """ staticfiles_installed = apps.is_installed("django.contrib.staticfiles") use_static_handler = options.get("use_static_handler", staticfiles_installed) insecure_serving = options.get("insecure_serving", False) if use_static_handler and (settings.DEBUG or insecure_serving): return StaticFilesWrapper(get_default_application()) else: return get_default_application()
async def send(self, message: Scope) -> Awaitable[Any]: """ 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.middleware.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.middleware.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.middleware.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, ) # Pass up the send return await self.real_send(message)
async def http_request(self, message: Scope) -> None: """ Async entrypoint - concatenates body fragments and hands off control to ``self.handle`` when the body has been completely received. """ if "body" in message: self.body.append(message["body"]) if not message.get("more_body"): try: await self.handle(b"".join(self.body)) finally: await self.disconnect() raise StopConsumer()
def login(scope: Scope, user: Optional[Union[AbstractBaseUser, AnonymousUser]], backend=None) -> None: """ Persist a user id and a backend in the request. This way a user doesn't have to re-authenticate on every request. Note that data set during the anonymous session is retained when the user logs in. """ if "session" not in scope: raise ValueError( "Cannot find session in scope. You should wrap your consumer in SessionMiddleware." ) session = scope["session"] session_auth_hash = "" if user is None: user = scope.get("user", None) if user is None: raise ValueError( "User must be passed as an argument or must be present in the scope." ) if hasattr(user, "get_session_auth_hash"): session_auth_hash = user.get_session_auth_hash() if SESSION_KEY in session: if _get_user_session_key(session) != user.pk or ( session_auth_hash and not constant_time_compare( session.get(HASH_SESSION_KEY, ""), session_auth_hash)): # To avoid reusing another user's session, create a new, empty # session if the existing session corresponds to a different # authenticated user. session.flush() else: session.cycle_key() try: backend = backend or user.backend except AttributeError: backends = _get_backends(return_tuples=True) if len(backends) == 1: _, backend = backends[0] else: raise ValueError( "You have multiple authentication backends configured and therefore must provide the `backend` " "argument or set the `backend` attribute on the user.") session[SESSION_KEY] = user._meta.pk.value_to_string(user) session[BACKEND_SESSION_KEY] = backend session[HASH_SESSION_KEY] = session_auth_hash scope["user"] = user # note this does not reset the CSRF_COOKIE/Token user_logged_in.send(sender=user.__class__, request=None, user=user)
def __call__(self, scope: Scope): # 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("ascii")) break else: # No cookie header found - add an empty default. cookies = {} # Return inner application return self.inner(dict(scope, cookies=cookies))
def __call__(self, scope: Scope): # Make sure the scope is of type websocket if scope["type"] != "websocket": raise ValueError( "You cannot use OriginValidator on a non-WebSocket connection") # Extract the Origin header parsed_origin = None for header_name, header_value in scope.get("headers", []): if header_name == b"origin": try: # Set ResultParse parsed_origin = urlparse(header_value.decode("ascii")) except UnicodeDecodeError: pass # Check to see if the origin header is valid if self.valid_origin(parsed_origin): # Pass control to the application return self.application(scope) else: # Deny the connection return WebsocketDenier(scope)
def logout(scope: Scope) -> None: """ Remove the authenticated user's ID from the request and flush their session data. """ if "session" not in scope: raise ValueError( "Login cannot find session in scope. You should wrap your consumer in SessionMiddleware." ) session = scope["session"] # Dispatch the signal before the user is logged out so the receivers have a # chance to find out *who* logged out. user = scope.get("user", None) if hasattr(user, "is_authenticated") and not user.is_authenticated: user = None if user is not None: user_logged_out.send(sender=user.__class__, request=None, user=user) # remember language choice saved to session language = session.get(LANGUAGE_SESSION_KEY) session.flush() if language is not None: session[LANGUAGE_SESSION_KEY] = language if "user" in scope: scope["user"] = AnonymousUser()