Coverage for distro_tracker/signon/middleware.py: 95%
28 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-01-12 09:15 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-01-12 09:15 +0000
1# Copyright 2020-2023 Enrico Zini <enrico@debian.org>
2# Copyright 2023 The Debusine Developers
3# See the COPYRIGHT file at the top-level directory of this distribution
4#
5# This file is part of Distro Tracker. It is subject to the license terms
6# in the LICENSE file found in the top-level directory of this
7# distribution and at https://deb.li/DTLicense. No part of Distro Tracker,
8# including this file, may be copied, modified, propagated, or distributed
9# except according to the terms contained in the LICENSE file.
11"""
12Authentication middleware that authenticates using signon Providers.
14It adds a `request.signon` member that is a Signon object, providing an entry
15point for managing externally authenticated identities.
16"""
17from collections.abc import Callable
18from typing import Protocol, cast, runtime_checkable
20import django.http
21from django.conf import settings
22from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
23from django.utils.module_loading import import_string
25from distro_tracker.signon.signon import Signon
28@runtime_checkable
29class RequestSignonProtocol(Protocol):
30 """A Django request that has been processed by :class:`SignonMiddleware`."""
32 signon: Signon
35class SignonMiddleware:
36 """Authenticate via external signon providers."""
38 signon_class: type[Signon]
40 def __init__(
41 self,
42 get_response: Callable[
43 [django.http.HttpRequest], django.http.HttpResponse
44 ],
45 ) -> None:
46 """Middleware API entry point."""
47 self.providers = getattr(settings, "SIGNON_PROVIDERS", ())
48 if not self.providers:
49 raise MiddlewareNotUsed()
51 # Find the Signon class to use. This allows customizing behaviour by
52 # subclassing Signon
53 if ( 53 ↛ 56line 53 didn't jump to line 56, because the condition on line 53 was never true
54 signon_class_path := getattr(settings, "SIGNON_CLASS", None)
55 ) is None:
56 self.signon_class = Signon
57 else:
58 self.signon_class = import_string(signon_class_path)
59 if not issubclass(self.signon_class, Signon):
60 raise ImproperlyConfigured(
61 f"{signon_class_path} is not a subclass of Signon"
62 )
64 self.get_response = get_response
66 def __call__(
67 self, request: django.http.HttpRequest
68 ) -> django.http.HttpResponse:
69 """Middleware API entry point."""
70 # AuthenticationMiddleware is required so that request.user exists.
71 if not hasattr(request, 'user'):
72 raise ImproperlyConfigured(
73 "The signon middleware requires the authentication middleware"
74 " to be installed. Edit your MIDDLEWARE setting to insert"
75 " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
76 " before the SignonMiddleware class."
77 )
79 # Add request.signon
80 cast(RequestSignonProtocol, request).signon = self.signon_class(request)
82 return self.get_response(request)