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:
1 parent
5210186126
commit
dd5c5629f3
2 files changed
+125
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
Reference in new issue
Block a user