arthexis 0.1.26__py3-none-any.whl → 0.1.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arthexis might be problematic. Click here for more details.

pages/views.py CHANGED
@@ -41,7 +41,11 @@ from django.test import RequestFactory, signals as test_signals
41
41
  from django.urls import NoReverseMatch, reverse
42
42
  from django.utils import timezone
43
43
  from django.utils.encoding import force_bytes, force_str
44
- from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
44
+ from django.utils.http import (
45
+ url_has_allowed_host_and_scheme,
46
+ urlsafe_base64_decode,
47
+ urlsafe_base64_encode,
48
+ )
45
49
  from core import mailer, public_wifi
46
50
  from core.backends import TOTP_DEVICE_NAME
47
51
  from django.utils.translation import get_language, gettext as _
@@ -69,12 +73,33 @@ from core.models import (
69
73
  from ocpp.models import Charger
70
74
  from .utils import get_original_referer
71
75
 
76
+
77
+ class _GraphvizDeprecationFilter(logging.Filter):
78
+ """Filter out Graphviz debug logs about positional arg deprecations."""
79
+
80
+ _MESSAGE_PREFIX = "deprecate positional args:"
81
+
82
+ def filter(self, record: logging.LogRecord) -> bool: # pragma: no cover - logging hook
83
+ try:
84
+ message = record.getMessage()
85
+ except Exception: # pragma: no cover - defensive fallback
86
+ return True
87
+ return not message.startswith(self._MESSAGE_PREFIX)
88
+
89
+
72
90
  try: # pragma: no cover - optional dependency guard
73
91
  from graphviz import Digraph
74
92
  from graphviz.backend import CalledProcessError, ExecutableNotFound
75
93
  except ImportError: # pragma: no cover - handled gracefully in views
76
94
  Digraph = None
77
95
  CalledProcessError = ExecutableNotFound = None
96
+ else:
97
+ graphviz_logger = logging.getLogger("graphviz._tools")
98
+ if not any(
99
+ isinstance(existing_filter, _GraphvizDeprecationFilter)
100
+ for existing_filter in graphviz_logger.filters
101
+ ):
102
+ graphviz_logger.addFilter(_GraphvizDeprecationFilter())
78
103
 
79
104
  import markdown
80
105
  from django.utils._os import safe_join
@@ -919,6 +944,17 @@ class CustomLoginView(LoginView):
919
944
  "restricted_notice": restricted_notice,
920
945
  }
921
946
  )
947
+ node = Node.get_local()
948
+ has_rfid_scanner = False
949
+ if node:
950
+ try:
951
+ node.refresh_features()
952
+ except Exception:
953
+ logger.exception("Unable to refresh node features for login page")
954
+ has_rfid_scanner = node.has_feature("rfid-scanner")
955
+ context["show_rfid_login"] = has_rfid_scanner
956
+ if has_rfid_scanner:
957
+ context["rfid_login_url"] = reverse("pages:rfid-login")
922
958
  return context
923
959
 
924
960
  def get_success_url(self):
@@ -940,6 +976,31 @@ class CustomLoginView(LoginView):
940
976
  login_view = CustomLoginView.as_view()
941
977
 
942
978
 
979
+ @ensure_csrf_cookie
980
+ def rfid_login_page(request):
981
+ node = Node.get_local()
982
+ if not node or not node.has_feature("rfid-scanner"):
983
+ raise Http404
984
+ if request.user.is_authenticated:
985
+ return redirect(reverse("admin:index") if request.user.is_staff else "/")
986
+ redirect_field_name = CustomLoginView.redirect_field_name
987
+ redirect_target = request.GET.get(redirect_field_name, "")
988
+ if redirect_target and not url_has_allowed_host_and_scheme(
989
+ redirect_target,
990
+ allowed_hosts={request.get_host()},
991
+ require_https=request.is_secure(),
992
+ ):
993
+ redirect_target = ""
994
+ context = {
995
+ "login_api_url": reverse("rfid-login"),
996
+ "scan_api_url": reverse("rfid-scan-next"),
997
+ "redirect_field_name": redirect_field_name,
998
+ "redirect_target": redirect_target,
999
+ "back_url": reverse("pages:login"),
1000
+ }
1001
+ return render(request, "pages/rfid_login.html", context)
1002
+
1003
+
943
1004
  @staff_member_required
944
1005
  def authenticator_setup(request):
945
1006
  """Allow staff to enroll an authenticator app for TOTP logins."""
@@ -1493,15 +1554,17 @@ def client_report(request):
1493
1554
  return HttpResponseRedirect(redirect_url)
1494
1555
  download_url = None
1495
1556
  download_param = request.GET.get("download")
1496
- if download_param and request.user.is_authenticated:
1557
+ if download_param:
1497
1558
  try:
1498
1559
  download_id = int(download_param)
1499
1560
  except (TypeError, ValueError):
1500
1561
  download_id = None
1501
- if download_id:
1562
+ if download_id and request.user.is_authenticated:
1502
1563
  download_url = reverse(
1503
1564
  "pages:client-report-download", args=[download_id]
1504
1565
  )
1566
+ if download_url:
1567
+ setattr(request, "live_update_interval", None)
1505
1568
 
1506
1569
  try:
1507
1570
  login_url = reverse("pages:login")