Einstieg Schritt 2: WLAN der Organisation als Einstellung, an alle Geräte (Rolle org-wlan)

Migration 0074: organizations.org_wlan (Optionen der Rolle, Passwort
verschlüsselt). GET/PUT /api/v1/organizations/{id}/org-wlan; im
Auftragskatalog gilt es für org-wlan, solange OE/Gruppe nichts festlegt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-25 12:41:05 +01:00
1 parent 5210186126
commit dd5c5629f3
2 files changed
+125

No files matched your search

+109
View File
@@ -1645,6 +1645,11 @@ def fetch_auftragskatalog_state(device_id: str):
aktiv = workspace_default
optionen = {}
if key == ORG_WLAN_MERKMAL and merkmal_id not in overrides:
org_wlan = org_wlan_fuer_geraet(device_id)
if org_wlan:
aktiv, optionen = True, org_wlan
optionen = decrypt_sensitive_optionen(optionen)
if key == VEYON_MERKMAL and aktiv:
optionen = veyon_optionen_anreichern(device_id, merkmal_id, optionen)
@@ -4173,6 +4178,87 @@ def create_organization(name: str, kundengruppe_id: str | None = None):
return {"id": str(organization_id), "name": name, "standard_workspace": standard}
# WLAN der Organisation (Einstieg vereinfachen, Schritt 2, Migration 0074).
# Gespeichert in der Form der Merkmal-Optionen der Rolle org-wlan; ein am
# Merkmal selbst (OE/Gruppe) gesetzter Wert hat Vorrang.
ORG_WLAN_MERKMAL = "org-wlan"
def org_wlan_pruefen(ssid: str, passwort: str, offen: bool) -> str | None:
if not ssid:
return None
if len(ssid.encode("utf-8")) > 32 or "\n" in ssid or "\r" in ssid:
return "Der WLAN-Name (SSID) darf höchstens 32 Bytes lang sein."
if offen:
return None
if not passwort:
return "Bitte das WLAN-Passwort eingeben oder „Offenes WLAN“ wählen."
if "\n" in passwort or "\r" in passwort or not (8 <= len(passwort) <= 63 or re.fullmatch(r"[0-9A-Fa-f]{64}", passwort)):
return "Das WLAN-Passwort muss 8 bis 63 Zeichen lang sein."
return None
def fetch_org_wlan(organization_id: str, mit_passwort: bool = False) -> dict | None:
"""{"ssid", "offen", "hat_passwort"[, "passwort"]} oder None ohne WLAN."""
with get_database_connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT org_wlan FROM organizations WHERE id = %s", (organization_id,))
row = cur.fetchone()
if not row or not row[0] or not row[0].get("wlan_ssid"):
return None
gespeichert = row[0]
ergebnis = {
"ssid": gespeichert["wlan_ssid"],
"offen": not gespeichert.get("wlan_passwort"),
"hat_passwort": bool(gespeichert.get("wlan_passwort")),
}
if mit_passwort:
ergebnis["passwort"] = decrypt_sensitive_optionen(gespeichert).get("wlan_passwort") or ""
return ergebnis
def set_org_wlan(organization_id: str, ssid: str, passwort: str, offen: bool) -> str | None:
"""Speichert das WLAN der Organisation; leere SSID entfernt es. Ein leeres
Passwort bei gleicher SSID behält das gespeicherte. Fehlermeldung oder None."""
ssid = (ssid or "").strip()
with get_database_connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT org_wlan FROM organizations WHERE id = %s FOR UPDATE", (organization_id,))
row = cur.fetchone()
if row is None:
return "Die Organisation wurde nicht gefunden."
bisher = row[0] or {}
if ssid and not offen and not passwort and bisher.get("wlan_ssid") == ssid and bisher.get("wlan_passwort"):
neu = bisher
else:
fehler = org_wlan_pruefen(ssid, passwort, offen)
if fehler:
return fehler
neu = encrypt_sensitive_optionen({"wlan_ssid": ssid, "wlan_passwort": "" if offen else passwort}) if ssid else None
cur.execute(
"UPDATE organizations SET org_wlan = %s, updated_at = now() WHERE id = %s",
(Jsonb(neu) if neu else None, organization_id),
)
return None
def org_wlan_fuer_geraet(device_id: str) -> dict | None:
"""Optionen für die Rolle org-wlan aus dem WLAN der Organisation des Geräts."""
with get_database_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT o.org_wlan FROM devices d JOIN organizations o ON o.id = d.organization_id WHERE d.id = %s",
(device_id,),
)
row = cur.fetchone()
if not row or not row[0] or not row[0].get("wlan_ssid"):
return None
return dict(row[0])
def standard_ausstattung_anlegen(cur, organization_id, standard_oe_id, kundengruppe_id) -> str | None:
"""
Einstieg vereinfachen, Schritt 1 (25.10.2026): Hat die Art der
@@ -8021,6 +8107,29 @@ def get_organization_endpoint(organization_id: str, authorization: str | None =
return {"success": True, "organization": organization}
class OrgWlanRequest(BaseModel):
ssid: str = ""
passwort: str = ""
offen: bool = False
@app.get("/api/v1/organizations/{organization_id}/org-wlan")
def get_org_wlan_endpoint(organization_id: str, mit_passwort: bool = False, authorization: str | None = Header(default=None)):
if not require_service_token(authorization):
return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."}
return {"success": True, "org_wlan": fetch_org_wlan(organization_id, mit_passwort)}
@app.put("/api/v1/organizations/{organization_id}/org-wlan")
def put_org_wlan_endpoint(organization_id: str, payload: OrgWlanRequest, authorization: str | None = Header(default=None)):
if not require_service_token(authorization):
return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."}
fehler = set_org_wlan(organization_id, payload.ssid, payload.passwort, payload.offen)
if fehler:
return {"success": False, "error": "ungueltig", "message": fehler}
return {"success": True, "org_wlan": fetch_org_wlan(organization_id)}
class SetAusrollstufeRequest(BaseModel):
ausrollstufe: str
+16
View File
@@ -0,0 +1,16 @@
BEGIN;
-- Einstieg vereinfachen, Schritt 2 (platform-docs roadmap/einstieg-
-- vereinfachen.md, 25.10.2026): Das WLAN der Organisation ist eine
-- Einstellung der Organisation, nicht mehr nur das WLAN vom
-- Installationsmedium. Alle Geräte bekommen es über die Rolle org-wlan,
-- auch per Kabel installierte (Befund Liebherr-83, 14.10.2026).
--
-- Form wie die Merkmal-Optionen der Rolle: {"wlan_ssid", "wlan_passwort"}
-- mit "enc:"-verschlüsseltem Passwort (encrypt_sensitive_optionen); NULL =
-- kein WLAN der Organisation. Ein am Merkmal "Org-WLAN" (OE/Gruppe)
-- gesetzter Wert hat weiter Vorrang.
ALTER TABLE organizations ADD COLUMN org_wlan JSONB;
COMMIT;