Coverage for website/thaliawebsite/views.py: 59.72%

62 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1"""General views for the website.""" 

2 

3from django.contrib import messages 

4from django.contrib.admin.views.decorators import staff_member_required 

5from django.contrib.auth.views import LogoutView as BaseLogoutView 

6from django.contrib.auth.views import PasswordResetView 

7from django.core.exceptions import PermissionDenied 

8from django.http import HttpResponse, HttpResponseForbidden 

9from django.shortcuts import redirect 

10from django.utils.decorators import method_decorator 

11from django.views.generic import ListView, TemplateView 

12from django.views.generic.base import View 

13 

14from django_otp import user_has_device 

15from django_ratelimit.decorators import ratelimit 

16from two_factor.views import LoginView 

17 

18 

19class IndexView(TemplateView): 

20 template_name = "index.html" 

21 

22 

23@method_decorator(staff_member_required, "dispatch") 

24class TestCrashView(View): 

25 """Test view to intentionally crash to test the error handling.""" 

26 

27 def dispatch(self, request, *args, **kwargs) -> HttpResponse: 

28 if not request.user.is_superuser: 

29 return HttpResponseForbidden("This is not for you") 

30 raise Exception("Test exception") 

31 

32 

33class PagedView(ListView): 

34 """A ListView with automatic pagination.""" 

35 

36 def get_context_data(self, **kwargs) -> dict: 

37 context = super().get_context_data(**kwargs) 

38 page = context["page_obj"].number 

39 paginator = context["paginator"] 

40 

41 # Show the two pages before and after the current page 

42 page_range_start = max(1, page - 2) 

43 page_range_stop = min(page + 3, paginator.num_pages + 1) 

44 

45 # Add extra pages if we show less than 5 pages 

46 page_range_start = min(page_range_start, page_range_stop - 5) 

47 page_range_start = max(1, page_range_start) 

48 

49 # Add extra pages if we still show less than 5 pages 

50 page_range_stop = max(page_range_stop, page_range_start + 5) 

51 page_range_stop = min(page_range_stop, paginator.num_pages + 1) 

52 

53 page_range = range(page_range_start, page_range_stop) 

54 

55 context.update( 

56 { 

57 "page_range": page_range, 

58 } 

59 ) 

60 

61 return context 

62 

63 

64class RateLimitedPasswordResetView(PasswordResetView): 

65 @method_decorator(ratelimit(key="ip", rate="5/m")) 

66 def post(self, request, *args, **kwargs): 

67 return super().post(request, *args, **kwargs) 

68 

69 

70class RateLimitedLoginView(LoginView): 

71 @method_decorator(ratelimit(key="ip", rate="50/m")) 

72 def post(self, request, *args, **kwargs): 

73 return super().post(request, *args, **kwargs) 

74 

75 

76class LogoutView(BaseLogoutView): 

77 # Allow GET logout still (this was deprecated in Django 5.0). 

78 http_method_names = ["get", "post", "options"] 

79 

80 def get(self, request, *args, **kwargs): 

81 return self.post(request, *args, **kwargs) 

82 

83 

84def rate_limited_view(request, *args, **kwargs): 

85 return HttpResponse("You are rate limited", status=429) 

86 

87 

88def admin_unauthorized_view(request): 

89 if not request.member: 

90 url = "/user/account/login" 

91 args = request.META.get("QUERY_STRING", "") 

92 if args: 

93 url = f"{url}?{args}" 

94 return redirect(url) 

95 elif not request.member.is_staff and not request.member.is_superuser: 

96 raise PermissionDenied("You are not allowed to access the administration page.") 

97 elif not user_has_device(request.member): 

98 messages.error( 

99 request, 

100 "You need to set up two-factor authentication to access the administration page.", 

101 ) 

102 return redirect("two_factor:setup") 

103 else: 

104 return redirect(request.GET.get("next", "/"))