Vogelalbum 1.0.0
Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine Sammlung, die sich von allein fuellt. Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert — ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten, gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte, Stufen und 15 Abzeichen. Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto, keine Telemetrie; die Bilder verlassen das eigene Netz nicht. Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und braucht deshalb weder eine erreichbare Adresse noch einen offenen Port — er laeuft hinter einem gewoehnlichen Router. Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs. AGPL-3.0.
This commit is contained in:
commit
cc9e0786c2
189 files changed
+29792
No files matched your search
@@ -0,0 +1,101 @@
|
||||
"""Dass die Absicherung im Betrieb auch wirklich anspringt.
|
||||
|
||||
🔑 **Warum das einen eigenen Test braucht.** Die Einstellungen hängen an
|
||||
``DEBUG=0``, und in der Entwicklung *und in den Tests* ist ``DJANGO_DEBUG``
|
||||
standardmäßig ``1``. Die ganze Absicherung läuft also in keinem einzigen
|
||||
normalen Testlauf — sie stünde richtig in der Datei und wäre nie geprüft.
|
||||
Genau die Sorte Sicherheitsnetz, die stumm kaputt ist.
|
||||
|
||||
Deshalb hier die Einstellungsdatei einmal frisch mit ``DJANGO_DEBUG=0``
|
||||
einlesen und nachsehen, was dabei herauskommt.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
|
||||
def betriebs_einstellungen():
|
||||
"""Lädt ``vogelalbum.settings`` neu, als liefe es im Betrieb."""
|
||||
umgebung = {
|
||||
"DJANGO_DEBUG": "0",
|
||||
# Im Betrieb ist ein eigener Schlüssel Pflicht, sonst bricht der
|
||||
# Import ab — hier einer, der nur in diesem Test lebt.
|
||||
"DJANGO_SECRET_KEY": "nur-fuer-diesen-test-" + "x" * 40,
|
||||
}
|
||||
with mock.patch.dict(os.environ, umgebung):
|
||||
modul = importlib.import_module("vogelalbum.settings")
|
||||
return importlib.reload(modul)
|
||||
|
||||
|
||||
class BetriebsEinstellungenTests(SimpleTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.betrieb = betriebs_einstellungen()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# ⚠️ Zurückladen, sonst sieht jeder nachfolgende Test die
|
||||
# Betriebsfassung — und scheitert an Dingen, die mit ihm nichts zu
|
||||
# tun haben.
|
||||
import vogelalbum.settings
|
||||
|
||||
importlib.reload(vogelalbum.settings)
|
||||
super().tearDownClass()
|
||||
|
||||
def test_debug_ist_aus(self):
|
||||
self.assertFalse(self.betrieb.DEBUG)
|
||||
|
||||
def test_cookies_nur_ueber_https(self):
|
||||
"""🔴 Der Kern: Die Umleitung auf https kommt zu spät.
|
||||
|
||||
Traefik antwortet mit 307 — da hat der Browser das Cookie aber schon
|
||||
mitgeschickt. Erst ``Secure`` hält ihn davon ab.
|
||||
"""
|
||||
self.assertTrue(self.betrieb.SESSION_COOKIE_SECURE)
|
||||
self.assertTrue(self.betrieb.CSRF_COOKIE_SECURE)
|
||||
|
||||
def test_hsts_ist_gesetzt(self):
|
||||
self.assertGreaterEqual(self.betrieb.SECURE_HSTS_SECONDS, 31536000)
|
||||
|
||||
def test_hsts_nicht_fuer_fremde_namen(self):
|
||||
"""Keine Zusagen für Namen, die es nicht gibt, und kein preload.
|
||||
|
||||
``includeSubDomains`` gilt auch für Namen, die noch niemand angelegt
|
||||
hat, und ``preload`` nimmt einem die Entscheidung aus der Hand: Aus
|
||||
fremden Listen kommt man nur langsam wieder heraus.
|
||||
"""
|
||||
self.assertFalse(self.betrieb.SECURE_HSTS_INCLUDE_SUBDOMAINS)
|
||||
self.assertFalse(self.betrieb.SECURE_HSTS_PRELOAD)
|
||||
|
||||
def test_proxy_kopf_wird_ausgewertet(self):
|
||||
"""Ohne das baut build_absolute_uri() http://-Adressen."""
|
||||
self.assertEqual(
|
||||
self.betrieb.SECURE_PROXY_SSL_HEADER,
|
||||
("HTTP_X_FORWARDED_PROTO", "https"),
|
||||
)
|
||||
|
||||
def test_referrer_bleibt_im_haus(self):
|
||||
self.assertEqual(self.betrieb.SECURE_REFERRER_POLICY, "same-origin")
|
||||
|
||||
def test_in_der_entwicklung_gilt_nichts_davon(self):
|
||||
"""⚠️ Sonst kann sich auf http://localhost niemand mehr anmelden.
|
||||
|
||||
Ein ``Secure``-Cookie wird über einfaches HTTP gar nicht gesetzt — die
|
||||
Anmeldung schlüge fehl, und die Ursache wäre ausgerechnet die
|
||||
Einstellung, die Sicherheit bringen soll.
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
# ⚠️ ``settings.DEBUG`` taugt hier NICHT als Probe: Der Testläufer
|
||||
# setzt es grundsätzlich auf False, egal was die Datei gerechnet hat.
|
||||
# Entscheidend ist, was der ``if not DEBUG``-Block beim IMPORT gesehen
|
||||
# hat — und das war ``DJANGO_DEBUG=1``. Also die Flaggen prüfen.
|
||||
self.assertFalse(getattr(settings, "SESSION_COOKIE_SECURE", False))
|
||||
self.assertFalse(getattr(settings, "CSRF_COOKIE_SECURE", False))
|
||||
self.assertEqual(getattr(settings, "SECURE_HSTS_SECONDS", 0), 0)
|
||||
Reference in new issue
Block a user