diff --git a/apps/tesm-license/pyproject.toml b/apps/tesm-license/pyproject.toml
index bfe23c2..8a85b5f 100644
--- a/apps/tesm-license/pyproject.toml
+++ b/apps/tesm-license/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tesm-license"
-version = "2.7.1"
+version = "2.7.2"
description = "TESM-Lizenzserver -- Kunden, Tickets, Lizenzen, Aktivierung"
requires-python = ">=3.11"
dependencies = [
diff --git a/apps/tesm-license/src/tesm_license/__init__.py b/apps/tesm-license/src/tesm_license/__init__.py
index 5dd39a1..8ade82f 100644
--- a/apps/tesm-license/src/tesm_license/__init__.py
+++ b/apps/tesm-license/src/tesm_license/__init__.py
@@ -204,7 +204,7 @@ def _status_pills(user: Any) -> list[dict[str, str]]:
def _startup_tasks(app: Flask) -> None:
extension = core(app)
if not extension.config.web_process:
- logger.info("Startaufgaben uebersprungen -- kein Webprozess.")
+ logger.info("Startaufgaben übersprungen – kein Webprozess.")
return
from .services import activation
diff --git a/apps/tesm-license/src/tesm_license/blueprints/api.py b/apps/tesm-license/src/tesm_license/blueprints/api.py
index ad8975a..8242f74 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/api.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/api.py
@@ -59,7 +59,7 @@ def _handle(action: str):
)
if request.content_length and request.content_length > MAX_BODY_BYTES:
- return jsonify({"error": "too_large", "message": "Anfrage zu gross."}), 413
+ return jsonify({"error": "too_large", "message": "Anfrage zu groß."}), 413
payload = request.get_json(silent=True)
if not isinstance(payload, dict):
diff --git a/apps/tesm-license/src/tesm_license/blueprints/backup.py b/apps/tesm-license/src/tesm_license/blueprints/backup.py
index a131a66..045b844 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/backup.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/backup.py
@@ -63,12 +63,12 @@ def export():
allowed = {category.key for category in _allowed_categories()}
chosen = [key for key in request.form.getlist("categories") if key in allowed]
if not chosen:
- flash("Es wurde keine Kategorie ausgewaehlt.", "warning")
+ flash("Es wurde keine Kategorie ausgewählt.", "warning")
return redirect(url_for("backup.overview"))
passphrase = request.form.get("passphrase") or ""
if passphrase != (request.form.get("passphrase_repeat") or ""):
- flash("Die beiden Passphrasen stimmen nicht ueberein.", "danger")
+ flash("Die beiden Passphrasen stimmen nicht überein.", "danger")
return redirect(url_for("backup.overview"))
try:
@@ -101,11 +101,11 @@ def import_preview():
conn = db()
upload = request.files.get("backup_file")
if upload is None or not upload.filename:
- flash("Keine Datei ausgewaehlt.", "danger")
+ flash("Keine Datei ausgewählt.", "danger")
return redirect(url_for("backup.overview"))
raw = upload.stream.read(MAX_IMPORT_BYTES + 1)
if len(raw) > MAX_IMPORT_BYTES:
- flash("Die Datei ist zu gross.", "danger")
+ flash("Die Datei ist zu groß.", "danger")
return redirect(url_for("backup.overview"))
try:
@@ -124,8 +124,8 @@ def import_preview():
scope="sicherung",
)
flash(
- f"Datei geoeffnet. Sie haben {transfer.PREVIEW_TTL_MINUTES} Minuten Zeit, die "
- "Uebernahme zu bestaetigen.",
+ f"Datei geöffnet. Sie haben {transfer.PREVIEW_TTL_MINUTES} Minuten Zeit, die "
+ "Übernahme zu bestätigen.",
"success",
)
return redirect(url_for("backup.overview"))
@@ -139,13 +139,13 @@ def import_apply():
conn = db()
token = session.get(PREVIEW_SESSION_KEY, "")
if not token:
- flash("Es liegt keine geoeffnete Sicherung vor.", "danger")
+ flash("Es liegt keine geöffnete Sicherung vor.", "danger")
return redirect(url_for("backup.overview"))
allowed = {category.key for category in _allowed_categories()}
chosen = [key for key in request.form.getlist("categories") if key in allowed]
if not chosen:
- flash("Es wurde keine Kategorie ausgewaehlt.", "warning")
+ flash("Es wurde keine Kategorie ausgewählt.", "warning")
return redirect(url_for("backup.overview"))
try:
@@ -172,7 +172,7 @@ def import_apply():
action="backup.imported",
detail="; ".join(
f"{key}: {counts['created']} neu, {counts['updated']} aktualisiert, "
- f"{counts['skipped']} uebersprungen"
+ f"{counts['skipped']} übersprungen"
for key, counts in report.items()
),
ip=client_ip(),
@@ -182,11 +182,11 @@ def import_apply():
lines = [
f"{transfer.CATEGORY_BY_KEY[key].label}: {counts['created']} neu, "
f"{counts['updated']} aktualisiert"
- + (f", {counts['skipped']} uebersprungen" if counts["skipped"] else "")
+ + (f", {counts['skipped']} übersprungen" if counts["skipped"] else "")
for key, counts in report.items()
if key in transfer.CATEGORY_BY_KEY
]
- flash("Uebernahme abgeschlossen. " + " | ".join(lines), "success")
+ flash("Übernahme abgeschlossen. " + " – ".join(lines), "success")
return redirect(url_for("backup.overview"))
diff --git a/apps/tesm-license/src/tesm_license/blueprints/customers.py b/apps/tesm-license/src/tesm_license/blueprints/customers.py
index a10e152..902affc 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/customers.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/customers.py
@@ -96,7 +96,7 @@ def update(customer_id: int):
severity="notice",
scope="kunde",
)
- flash("Aenderungen gespeichert." if changes else "Keine Aenderungen.",
+ flash("Änderungen gespeichert." if changes else "Keine Änderungen.",
"success" if changes else "info")
except issuance.IssuanceError as exc:
flash(str(exc), "danger")
@@ -119,7 +119,7 @@ def delete(customer_id: int):
severity="warning",
scope="kunde",
)
- flash("Kunde geloescht.", "success")
+ flash("Kunde gelöscht.", "success")
return redirect(url_for("customers.overview"))
except issuance.IssuanceError as exc:
flash(str(exc), "danger")
diff --git a/apps/tesm-license/src/tesm_license/blueprints/licenses.py b/apps/tesm-license/src/tesm_license/blueprints/licenses.py
index c6ffd13..dec8850 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/licenses.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/licenses.py
@@ -124,7 +124,7 @@ def issue(ticket_id: str):
if result.already_existed:
flash(
- "Zu diesem Ticket war bereits eine Lizenz offen -- sie wird angezeigt statt eine "
+ "Zu diesem Ticket war bereits eine Lizenz offen – sie wird angezeigt statt eine "
"zweite auszustellen.",
"info",
)
@@ -166,7 +166,7 @@ def download(license_id: str):
channel="download",
recipient=current_user.username,
actor=current_user.username,
- detail="Vollstaendiges Bundle" if "client_key" in bundle else "Nur Lizenzdokument",
+ detail="Vollständiges Bundle" if "client_key" in bundle else "Nur Lizenzdokument",
)
log_event("license.downloaded", target=license_id[:12], severity="notice")
return Response(
@@ -202,7 +202,7 @@ def resign(license_id: str):
try:
days = int(request.form["extend_days"])
except ValueError:
- flash("Die Verlaengerung muss eine Zahl sein.", "danger")
+ flash("Die Verlängerung muss eine Zahl sein.", "danger")
return redirect(url_for("licenses.detail", license_id=license_id))
if days > 0:
document = json.loads(str(row["license_document"]))
@@ -212,7 +212,7 @@ def resign(license_id: str):
changes["expires_at"] = lic.iso(base + timedelta(days=days))
if not changes:
- flash("Es wurde keine Aenderung angegeben.", "info")
+ flash("Es wurde keine Änderung angegeben.", "info")
return redirect(url_for("licenses.detail", license_id=license_id))
key = signing_key()
@@ -233,7 +233,7 @@ def resign(license_id: str):
)
reporting.report_soon(core())
flash(
- f"Neue Revision {updated['seq']} signiert. Der Client erhaelt sie beim naechsten "
+ f"Neue Revision {updated['seq']} signiert. Der Client erhält sie beim nächsten "
"Heartbeat.",
"success",
)
@@ -258,7 +258,7 @@ def revoke(license_id: str):
)
reporting.report_soon(core())
flash(
- "Lizenz widerrufen. Das Ticket bleibt offen -- eine neue Lizenz kann sofort "
+ "Lizenz widerrufen. Das Ticket bleibt offen – eine neue Lizenz kann sofort "
"ausgestellt werden.",
"success",
)
@@ -277,7 +277,7 @@ def delete(license_id: str):
return redirect(url_for("licenses.detail", license_id=license_id))
log_event("license.deleted", target=license_id[:12], severity="warning")
reporting.report_soon(core())
- flash("Lizenz geloescht.", "success")
+ flash("Lizenz gelöscht.", "success")
return redirect(url_for("licenses.overview"))
@@ -295,7 +295,7 @@ def send_mail(license_id: str):
abort(404)
recipient = (request.form.get("recipient") or str(row["contact_email"] or "")).strip()
if not recipient:
- flash("Es ist keine Empfaengeradresse hinterlegt.", "danger")
+ flash("Es ist keine Empfängeradresse hinterlegt.", "danger")
return redirect(url_for("licenses.detail", license_id=license_id))
try:
@@ -315,7 +315,7 @@ def send_mail(license_id: str):
"",
f"Lizenz-ID: {license_id}",
f"Typ: {type_labels().get(str(row['type']), str(row['type']))}",
- f"Gueltig bis: {row['expires_at']}"
+ f"Gültig bis: {row['expires_at']}"
+ (" (unbefristet)" if row["lifetime"] else ""),
]
if is_customer and row["ticket_id"]:
@@ -329,7 +329,7 @@ def send_mail(license_id: str):
"Ihre Stammdaten pflegen:",
customer_facing_url("portal.request_link"),
]
- lines += ["", "Freundliche Gruesse", vendor.get("name") or "Ihr Lizenzgeber"]
+ lines += ["", "Freundliche Grüße", vendor.get("name") or "Ihr Lizenzgeber"]
if vendor.get("phone"):
lines.append(f"Telefon: {vendor['phone']}")
if vendor.get("email"):
@@ -403,7 +403,7 @@ def manual_code():
if outcome.ok:
response_code = lic.encode_code(outcome.response)
ok = True
- message = "Antwortcode erzeugt. Bitte an den Kunden zuruecksenden."
+ message = "Antwortcode erzeugt. Bitte an den Kunden zurücksenden."
log_event(
f"license.offline_{payload.get('action')}",
target=str(payload.get("license_id") or "")[:12],
diff --git a/apps/tesm-license/src/tesm_license/blueprints/own_license.py b/apps/tesm-license/src/tesm_license/blueprints/own_license.py
index 9da479d..461253e 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/own_license.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/own_license.py
@@ -79,11 +79,11 @@ def upload():
upload_file = request.files.get("license_file")
if upload_file is None or not upload_file.filename:
- flash("Keine Datei ausgewaehlt.", "danger")
+ flash("Keine Datei ausgewählt.", "danger")
return redirect(url_for("own_license.overview"))
raw = upload_file.stream.read(MAX_UPLOAD_BYTES + 1)
if len(raw) > MAX_UPLOAD_BYTES:
- flash("Die Datei ist zu gross fuer eine Lizenzdatei.", "danger")
+ flash("Die Datei ist zu groß für eine Lizenzdatei.", "danger")
return redirect(url_for("own_license.overview"))
try:
diff --git a/apps/tesm-license/src/tesm_license/blueprints/portal.py b/apps/tesm-license/src/tesm_license/blueprints/portal.py
index bfac568..cf4473d 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/portal.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/portal.py
@@ -45,7 +45,7 @@ RATE_WINDOW_MINUTES = 15
GENERIC_ANSWER = (
"Falls diese E-Mail-Adresse bei uns hinterlegt ist, wurde soeben eine Nachricht mit "
- "einem {minutes} Minuten gueltigen Zugangslink verschickt."
+ "einem {minutes} Minuten gültigen Zugangslink verschickt."
)
EDITABLE_FIELDS = (
@@ -150,13 +150,13 @@ def request_link():
f"Guten Tag{(' ' + str(customer['contact_person'])) if customer['contact_person'] else ''},",
"",
"mit dem folgenden Link erreichen Sie Ihre Daten im Kundenportal.",
- f"Der Link ist {minutes} Minuten gueltig und laesst sich nur einmal verwenden:",
+ f"Der Link ist {minutes} Minuten gültig und lässt sich nur einmal verwenden:",
"",
access_url,
"",
"Wenn Sie diesen Zugang nicht angefragt haben, ignorieren Sie die Nachricht.",
"",
- "Freundliche Gruesse",
+ "Freundliche Grüße",
vendor.get("name") or "Ihr Lizenzgeber",
]
),
@@ -185,7 +185,7 @@ def access(token: str):
expires = _parse(row["expires_at"]) if row is not None else None
valid = row is not None and row["used_at"] is None and expires is not None and _now() < expires
if not valid:
- flash("Dieser Link ist ungueltig oder abgelaufen.", "danger")
+ flash("Dieser Link ist ungültig oder abgelaufen.", "danger")
return redirect(url_for("portal.request_link"))
minutes = int(extension.settings.get(conn, "portal_session_minutes") or 60)
@@ -247,7 +247,7 @@ def me():
ip=client_ip(),
severity="notice",
)
- flash("Ihre Angaben wurden gespeichert." if changes else "Keine Aenderungen.", "success")
+ flash("Ihre Angaben wurden gespeichert." if changes else "Keine Änderungen.", "success")
except issuance.IssuanceError as exc:
flash(str(exc), "danger")
return redirect(url_for("portal.me"))
diff --git a/apps/tesm-license/src/tesm_license/blueprints/selfservice.py b/apps/tesm-license/src/tesm_license/blueprints/selfservice.py
index ebcc34c..fb4e8ca 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/selfservice.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/selfservice.py
@@ -62,7 +62,7 @@ def _rate_guard() -> None:
with transaction():
ratelimit.record(conn, RATE_BUCKET, client_ip() or "unbekannt")
if verdict.blocked:
- abort(429, description="Zu viele Anfragen. Bitte spaeter erneut versuchen.")
+ abort(429, description="Zu viele Anfragen. Bitte später erneut versuchen.")
def _needs_offline(conn, ticket_id: str): # type: ignore[no-untyped-def]
@@ -141,8 +141,8 @@ def issue(ticket_id: str):
return redirect(url_for("selfservice.overview", ticket_id=ticket_id))
if issuance.ticket_has_open_license(conn, ticket_id):
flash(
- "Es existiert bereits eine gueltige Lizenz. Geben Sie diese zuerst frei, wenn Sie "
- "auf ein anderes System wechseln moechten.",
+ "Es existiert bereits eine gültige Lizenz. Geben Sie diese zuerst frei, wenn Sie "
+ "auf ein anderes System wechseln möchten.",
"warning",
)
return redirect(url_for("selfservice.overview", ticket_id=ticket_id))
@@ -238,12 +238,12 @@ def offline(ticket_id: str):
_log(
"selfservice.offline_rejected",
ticket,
- "Code gehoert nicht zu diesem Vorgang",
+ "Code gehört nicht zu diesem Vorgang",
severity="warning",
)
flash(
- "Dieser Code gehoert nicht zu Ihrem Vorgang. Bitte pruefen Sie, ob Sie ihn "
- "vollstaendig kopiert haben.",
+ "Dieser Code gehört nicht zu Ihrem Vorgang. Bitte prüfen Sie, ob Sie ihn "
+ "vollständig kopiert haben.",
"danger",
)
return redirect(url_for("selfservice.offline", ticket_id=ticket_id))
@@ -299,7 +299,7 @@ def revoke(ticket_id: str, license_id: str):
_log("license.revoked_self_service", ticket, license_id[:12], severity="warning")
reporting.report_soon(core())
flash(
- "Die Lizenz wurde freigegeben. Sie koennen jetzt eine neue Lizenz fuer Ihr neues System "
+ "Die Lizenz wurde freigegeben. Sie können jetzt eine neue Lizenz für Ihr neues System "
"ausstellen.",
"success",
)
diff --git a/apps/tesm-license/src/tesm_license/blueprints/settings.py b/apps/tesm-license/src/tesm_license/blueprints/settings.py
index de4b088..1f2f169 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/settings.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/settings.py
@@ -121,7 +121,7 @@ def system_save():
ip=client_ip(),
severity="notice",
)
- flash("Einstellungen gespeichert." if changed else "Keine Aenderungen.",
+ flash("Einstellungen gespeichert." if changed else "Keine Änderungen.",
"success" if changed else "info")
return redirect(url_for("settings.system"))
@@ -133,10 +133,10 @@ def set_hostname():
sysops = extension.sysops
hostname = (request.form.get("hostname") or "").strip()
if not hostname or len(hostname) > 63 or not hostname.replace("-", "").isalnum():
- flash("Der Hostname ist ungueltig.", "danger")
+ flash("Der Hostname ist ungültig.", "danger")
return redirect(url_for("settings.system"))
if sysops is None or not sysops.available:
- flash("Systemaktionen sind in dieser Umgebung nicht verfuegbar.", "warning")
+ flash("Systemaktionen sind in dieser Umgebung nicht verfügbar.", "warning")
return redirect(url_for("settings.system"))
result = sysops.set_hostname(hostname)
if result.ok:
@@ -158,7 +158,7 @@ def set_timezone():
flash("Unbekannte Zeitzone.", "danger")
return redirect(url_for("settings.system"))
if sysops is None or not sysops.available:
- flash("Systemaktionen sind in dieser Umgebung nicht verfuegbar.", "warning")
+ flash("Systemaktionen sind in dieser Umgebung nicht verfügbar.", "warning")
return redirect(url_for("settings.system"))
result = sysops.set_timezone(timezone)
if result.ok:
@@ -227,7 +227,7 @@ def vendor_save():
if suffix not in LOGO_MIME:
logo_problem = "Erlaubt sind PNG, JPG und SVG."
elif len(data) > MAX_LOGO_BYTES:
- logo_problem = f"Das Logo ist zu gross (maximal {MAX_LOGO_BYTES // 1024} KB)."
+ logo_problem = f"Das Logo ist zu groß (maximal {MAX_LOGO_BYTES // 1024} KB)."
elif not any(head.startswith(magic) for magic in LOGO_MAGIC):
logo_problem = "Die Datei ist kein Bild."
else:
@@ -245,7 +245,7 @@ def vendor_save():
log_event(
"settings.vendor_saved", detail=", ".join(changed), severity="notice", scope="anbieter"
)
- flash("Anbieterdaten gespeichert." if changed else "Keine Aenderungen.",
+ flash("Anbieterdaten gespeichert." if changed else "Keine Änderungen.",
"success" if changed else "info")
return redirect(url_for("settings.vendor"))
@@ -297,7 +297,7 @@ def mail_save():
mail.store_secret(conn, request.form.get("client_secret"))
changed.append("client_secret")
log_event("settings.mail_saved", detail=", ".join(changed), severity="notice", scope="mail")
- flash("E-Mail-Einstellungen gespeichert." if changed else "Keine Aenderungen.",
+ flash("E-Mail-Einstellungen gespeichert." if changed else "Keine Änderungen.",
"success" if changed else "info")
return redirect(url_for("settings.mail_settings"))
diff --git a/apps/tesm-license/src/tesm_license/blueprints/tickets.py b/apps/tesm-license/src/tesm_license/blueprints/tickets.py
index 85387bf..ef23f17 100644
--- a/apps/tesm-license/src/tesm_license/blueprints/tickets.py
+++ b/apps/tesm-license/src/tesm_license/blueprints/tickets.py
@@ -106,9 +106,9 @@ def _endpoint_missing() -> str:
if master_endpoint():
return ""
return (
- "Es ist kein oeffentlicher Endpunkt hinterlegt (Einstellungen -> Anbieter). "
- "Solange er fehlt, koennen keine Lizenzen ausgestellt werden: der Endpunkt wird "
- "in die Lizenz signiert und laesst sich spaeter nicht nachtragen."
+ "Es ist kein öffentlicher Endpunkt hinterlegt (Einstellungen -> Anbieter). "
+ "Solange er fehlt, können keine Lizenzen ausgestellt werden: der Endpunkt wird "
+ "in die Lizenz signiert und lässt sich später nicht nachtragen."
)
@@ -176,9 +176,9 @@ def update(ticket_id: str):
)
reporting.report_soon(core())
flash(
- "Gespeichert. Die Aenderung wirkt auf die naechste Ausstellung."
+ "Gespeichert. Die Änderung wirkt auf die nächste Ausstellung."
if changes
- else "Keine Aenderungen.",
+ else "Keine Änderungen.",
"success" if changes else "info",
)
except (issuance.IssuanceError, ValueError) as exc:
@@ -205,7 +205,7 @@ def toggle_block(ticket_id: str):
)
reporting.report_soon(core())
flash(
- "Ticket gesperrt -- es lassen sich keine neuen Lizenzen mehr ausstellen."
+ "Ticket gesperrt – es lassen sich keine neuen Lizenzen mehr ausstellen."
if new_status == "blocked"
else "Ticket entsperrt.",
"success",
@@ -231,7 +231,7 @@ def delete(ticket_id: str):
scope="ticket",
)
reporting.report_soon(core())
- flash("Ticket und seine Lizenzhistorie geloescht.", "success")
+ flash("Ticket und seine Lizenzhistorie gelöscht.", "success")
return redirect(url_for("tickets.overview"))
except issuance.IssuanceError as exc:
flash(str(exc), "danger")
diff --git a/apps/tesm-license/src/tesm_license/cli.py b/apps/tesm-license/src/tesm_license/cli.py
index 2cb5cca..85ea113 100644
--- a/apps/tesm-license/src/tesm_license/cli.py
+++ b/apps/tesm-license/src/tesm_license/cli.py
@@ -42,22 +42,22 @@ def cmd_init_key(args: argparse.Namespace) -> int:
path = master_key_path(extension.config)
if path.exists() and not args.force:
key = master_key.load(path)
- print(f"Signaturschluessel existiert bereits: {key.key_id} ({path})")
- print("Ein Austausch macht ALLE bereits ausgestellten Lizenzen unpruefbar.")
+ print(f"Signaturschlüssel existiert bereits: {key.key_id} ({path})")
+ print("Ein Austausch macht ALLE bereits ausgestellten Lizenzen unprüfbar.")
return 0
if path.exists() and args.force:
print(
- "ABBRUCH: --force auf einen bestehenden Signaturschluessel wird nicht unterstuetzt.\n"
- "Ein neuer Schluessel entwertet jede ausgestellte Lizenz. Bitte die Datei bewusst\n"
+ "ABBRUCH: --force auf einen bestehenden Signaturschlüssel wird nicht unterstützt.\n"
+ "Ein neuer Schlüssel entwertet jede ausgestellte Lizenz. Bitte die Datei bewusst\n"
"von Hand beiseite legen, wenn das wirklich gewollt ist.",
file=sys.stderr,
)
return 2
key = master_key.ensure(path)
- print(f"Signaturschluessel erzeugt: {key.key_id}")
+ print(f"Signaturschlüssel erzeugt: {key.key_id}")
print(f"Datei: {path}")
- print("Bitte SOFORT sichern -- ohne sie ist keine ausgestellte Lizenz mehr pruefbar.")
- print(f"Oeffentlicher Schluessel (zum Pinnen beim Kunden):\n{key.public_key}")
+ print("Bitte SOFORT sichern – ohne sie ist keine ausgestellte Lizenz mehr prüfbar.")
+ print(f"Öffentlicher Schlüssel (zum Pinnen beim Kunden):\n{key.public_key}")
return 0
@@ -65,11 +65,11 @@ def cmd_bootstrap(args: argparse.Namespace) -> int:
"""Stellt die eigene Lizenz dieser Wurzelinstanz aus -- nur in der Master-Ausgabe."""
if not is_master_edition():
for zeile in (
- "Diese Ausgabe ist fuer Sub-Lizenzserver und kann keine Master-Lizenz erzeugen.",
+ "Diese Ausgabe ist für Sub-Lizenzserver und kann keine Master-Lizenz erzeugen.",
"",
"Ein Sub-Lizenzserver bekommt seine Lizenz vom Master-Lizenzserver und spielt",
"sie unter 'Eigene Lizenz' ein. Danach stellt er selbst Kundenlizenzen aus --",
- "mit seinem eigenen Signaturschluessel, der beim Installieren entstanden ist.",
+ "mit seinem eigenen Signaturschlüssel, der beim Installieren entstanden ist.",
"",
"Die Master-Ausgabe liegt im Repository tesm-license-master.",
):
@@ -83,10 +83,10 @@ def cmd_bootstrap(args: argparse.Namespace) -> int:
app, extension, days=int(args.days), name=args.name
)
print(f"Master-Lizenz ausgestellt: {document['license_id']}")
- print(f"Gueltig bis: {document['expires_at']}")
+ print(f"Gültig bis: {document['expires_at']}")
print(f"Datei: {extension.config.license_path}")
if state is not None:
- print(f"Zustand: gueltig={state.valid}, aktiviert={state.activated}, Rolle={state.role}")
+ print(f"Zustand: gültig={state.valid}, aktiviert={state.activated}, Rolle={state.role}")
return 0
@@ -98,7 +98,7 @@ def cmd_create_admin(args: argparse.Namespace) -> int:
return 2
password = args.password or getpass.getpass("Passwort: ")
if not args.password and password != getpass.getpass("Passwort wiederholen: "):
- print("Die Passwoerter stimmen nicht ueberein.", file=sys.stderr)
+ print("Die Passwörter stimmen nicht überein.", file=sys.stderr)
return 2
problems = extension.password_policy.check(password, username=username)
if problems and not args.force:
@@ -180,7 +180,7 @@ def cmd_status(args: argparse.Namespace) -> int:
+ ("unversehrt" if chain["ok"] else f"GEBROCHEN bei {chain['broken_at']}")
)
print(
- "Signaturschluessel: "
+ "Signaturschlüssel: "
+ (str(key_health.get("key_id")) if key_health.get("present") else "FEHLT")
)
for problem in key_health.get("problems", []):
@@ -253,17 +253,17 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
except legacy_import.LegacyImportError as exc:
if args.adopt_key:
raise
- print(f"Hinweis: alter Signaturschluessel nicht lesbar ({exc}).")
+ print(f"Hinweis: alter Signaturschlüssel nicht lesbar ({exc}).")
if key_path.is_file():
existing = master_key.load(key_path)
report.key_id = existing.key_id
if legacy_public and existing.public_key == legacy_public:
- print(f"Signaturschluessel stimmt bereits ueberein ({existing.key_id}).")
+ print(f"Signaturschlüssel stimmt bereits überein ({existing.key_id}).")
elif not legacy_public:
issuer_changes = True
print(
- f"Es wird mit dem vorhandenen Signaturschluessel {existing.key_id} "
+ f"Es wird mit dem vorhandenen Signaturschlüssel {existing.key_id} "
"gearbeitet."
)
else:
@@ -271,7 +271,7 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
# ausgestellte Lizenz waere danach unpruefbar.
issuer_changes = True
print(
- f"Diese Installation hat einen eigenen Signaturschluessel "
+ f"Diese Installation hat einen eigenen Signaturschlüssel "
f"({existing.key_id}); der alte war {lic.key_id(legacy_public)}. "
"Es wird mit dem vorhandenen weitergearbeitet."
)
@@ -279,12 +279,12 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
legacy_private, legacy_public = legacy_import.read_legacy_key(root)
report.key_id = legacy_import.adopt_key(legacy_private, legacy_public, key_path)
report.key_adopted = True
- print(f"Signaturschluessel uebernommen: {report.key_id}")
+ print(f"Signaturschlüssel übernommen: {report.key_id}")
else:
print(
- "Diese Installation hat noch keinen Signaturschluessel. Mit --adopt-key "
- "wird der alte uebernommen -- dann bleibt die Anbieteridentitaet erhalten "
- "und Kunden muessen keinen Ausstellerwechsel bestaetigen.",
+ "Diese Installation hat noch keinen Signaturschlüssel. Mit --adopt-key "
+ "wird der alte übernommen – dann bleibt die Anbieteridentität erhalten "
+ "und Kunden müssen keinen Ausstellerwechsel bestätigen.",
file=sys.stderr,
)
return 2
@@ -312,7 +312,7 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
# Uebernahme.
for einstellung, wert in uebernommen.items():
gekuerzt = wert if len(wert) <= 60 else wert[:57] + "..."
- print(f"Uebernommen: {einstellung} = {gekuerzt}")
+ print(f"Übernommen: {einstellung} = {gekuerzt}")
vendor = {
"name": str(extension.settings.get(conn, "vendor_name") or "Eigenbetrieb"),
@@ -326,7 +326,7 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
print(
"Es ist kein Endpunkt hinterlegt, und die alte Installation nennt "
"auch keinen. Bitte unter Einstellungen -> Anbieter eintragen: ohne "
- "ihn koennen uebernommene Lizenzen sich spaeter nicht aktivieren.",
+ "ihn können übernommene Lizenzen sich später nicht aktivieren.",
file=sys.stderr,
)
return 2
@@ -347,10 +347,10 @@ def cmd_migrate_legacy(args: argparse.Namespace) -> int:
result.key_id = report.key_id
if issuer_changes:
result.warnings.append(
- "Die Lizenzen sind mit einem anderen Schluessel signiert als "
- "frueher. Kunden, die den alten Aussteller gepinnt haben, muessen "
- "beim Einspielen einmal 'Wechsel des Ausstellers ausdruecklich "
- "zulassen' bestaetigen."
+ "Die Lizenzen sind mit einem anderen Schlüssel signiert als "
+ "früher. Kunden, die den alten Aussteller gepinnt haben, müssen "
+ "beim Einspielen einmal 'Wechsel des Ausstellers ausdrücklich "
+ "zulassen' bestätigen."
)
if args.dry_run:
# Nichts festschreiben -- der Lauf war nur zur Ansicht.
@@ -385,7 +385,7 @@ def build_parser() -> argparse.ArgumentParser:
)
sub = parser.add_subparsers(dest="command", required=True)
- init_key = sub.add_parser("init-key", help="Signaturschluessel erzeugen (einmalig)")
+ init_key = sub.add_parser("init-key", help="Signaturschlüssel erzeugen (einmalig)")
init_key.add_argument("--force", action="store_true")
init_key.set_defaults(func=cmd_init_key)
@@ -401,7 +401,7 @@ def build_parser() -> argparse.ArgumentParser:
bootstrap.add_argument("--name", default="Lizenzserver (Eigenbetrieb)")
bootstrap.set_defaults(func=cmd_bootstrap)
- admin = sub.add_parser("create-admin", help="Administratorkonto anlegen oder zuruecksetzen")
+ admin = sub.add_parser("create-admin", help="Administratorkonto anlegen oder zurücksetzen")
admin.add_argument("--username")
admin.add_argument("--password")
admin.add_argument("--force", action="store_true")
@@ -411,16 +411,16 @@ def build_parser() -> argparse.ArgumentParser:
status.add_argument("--json", action="store_true")
status.set_defaults(func=cmd_status)
- house = sub.add_parser("housekeeping", help="Laufzeitdaten aufraeumen")
+ house = sub.add_parser("housekeeping", help="Laufzeitdaten aufräumen")
house.set_defaults(func=cmd_housekeeping)
migrate = sub.add_parser(
"migrate-legacy",
- help="Lizenzserver der Vorgaengerfassung uebernehmen",
+ help="Lizenzserver der Vorgängerfassung übernehmen",
description=(
- "Uebernimmt Signaturschluessel, Kunden, Tickets und Lizenzen aus einer alten "
+ "Übernimmt Signaturschlüssel, Kunden, Tickets und Lizenzen aus einer alten "
"Installation. Die alte Installation wird nur gelesen. Jede Lizenz wird im "
- "neuen Format neu signiert -- die Kunden brauchen danach eine neue "
+ "neuen Format neu signiert – die Kunden brauchen danach eine neue "
"Lizenzdatei, weil die alte Signatur nicht mehr gilt."
),
)
@@ -430,13 +430,13 @@ def build_parser() -> argparse.ArgumentParser:
migrate.add_argument(
"--adopt-key",
action="store_true",
- help="Den alten Signaturschluessel uebernehmen. Nur moeglich, solange diese "
- "Installation noch keinen eigenen hat -- ein vorhandener wird nie ueberschrieben.",
+ help="Den alten Signaturschlüssel übernehmen. Nur möglich, solange diese "
+ "Installation noch keinen eigenen hat – ein vorhandener wird nie überschrieben.",
)
migrate.add_argument(
"--dry-run",
action="store_true",
- help="Nur anzeigen, was passieren wuerde -- nichts schreiben",
+ help="Nur anzeigen, was passieren würde – nichts schreiben",
)
migrate.set_defaults(func=cmd_migrate_legacy)
diff --git a/apps/tesm-license/src/tesm_license/permissions.py b/apps/tesm-license/src/tesm_license/permissions.py
index 1a10cce..c774221 100644
--- a/apps/tesm-license/src/tesm_license/permissions.py
+++ b/apps/tesm-license/src/tesm_license/permissions.py
@@ -30,18 +30,18 @@ CUSTOMERS = Resource(
label="Kunden",
actions=(A.VIEW, A.CREATE, A.EDIT, A.DELETE),
hint="Stammdaten der Kundeninstanzen",
- action_hints={A.DELETE: "Nur moeglich, solange kein Ticket am Kunden haengt."},
+ action_hints={A.DELETE: "Nur möglich, solange kein Ticket am Kunden hängt."},
)
TICKETS = Resource(
key="tickets",
label="Tickets",
actions=(A.VIEW, A.CREATE, A.EDIT, A.DELETE),
- hint="Dauerhafte Anspruechte auf eine Lizenz",
+ hint="Dauerhafte Ansprüche auf eine Lizenz",
action_hints={
- A.EDIT: "Parameter aendern und Ticket sperren/entsperren. Wirkt erst auf die "
- "naechste Ausstellung -- bereits ausgestellte Lizenzen bleiben unveraendert.",
- A.DELETE: "Nur moeglich, wenn keine offene Lizenz mehr existiert.",
+ A.EDIT: "Parameter ändern und Ticket sperren/entsperren. Wirkt erst auf die "
+ "nächste Ausstellung – bereits ausgestellte Lizenzen bleiben unverändert.",
+ A.DELETE: "Nur möglich, wenn keine offene Lizenz mehr existiert.",
},
)
@@ -62,7 +62,7 @@ PORTAL = Resource(
key="portal",
label="Kundenportal",
actions=(A.VIEW, A.EXECUTE),
- hint="Zugangslinks fuer Kunden",
+ hint="Zugangslinks für Kunden",
action_hints={A.EXECUTE: "Zugangslink erzeugen und versenden."},
)
@@ -79,7 +79,7 @@ MAIL = Resource(
actions=(A.VIEW, A.EDIT, A.EXECUTE, A.SECRETS),
hint="Microsoft Graph (App-only)",
action_hints={
- A.EXECUTE: "Verbindungstest ausfuehren.",
+ A.EXECUTE: "Verbindungstest ausführen.",
A.SECRETS: "Client-Secret setzen.",
},
)
@@ -88,7 +88,7 @@ VENDOR = Resource(
key="settings_vendor",
label="Anbieterdaten",
actions=(A.VIEW, A.EDIT),
- hint="Name, Kontakt, Logo und oeffentlicher Endpunkt -- reisen in jeder Lizenz mit",
+ hint="Name, Kontakt, Logo und öffentlicher Endpunkt – reisen in jeder Lizenz mit",
)
LICENSING_AREA = Area(
@@ -144,7 +144,7 @@ ROLE_PRESETS: tuple[RolePreset, ...] = (
RolePreset(
key="auditor",
name="Revision",
- description="Nur lesen -- inklusive Aenderungsprotokoll und Diagnose.",
+ description="Nur lesen – inklusive Änderungsprotokoll und Diagnose.",
grants=(
("customers", (A.VIEW,)),
("tickets", (A.VIEW,)),
@@ -161,7 +161,7 @@ ROLE_PRESETS: tuple[RolePreset, ...] = (
RolePreset(
key="administration",
name="Administration",
- description="Benutzer, Gruppen und Einstellungen -- ohne Admin-Kennzeichen.",
+ description="Benutzer, Gruppen und Einstellungen – ohne Admin-Kennzeichen.",
grants=(
("users", "*"),
("groups", "*"),
diff --git a/apps/tesm-license/src/tesm_license/services/activation.py b/apps/tesm-license/src/tesm_license/services/activation.py
index 5aeb7fc..9b3f8d0 100644
--- a/apps/tesm-license/src/tesm_license/services/activation.py
+++ b/apps/tesm-license/src/tesm_license/services/activation.py
@@ -132,7 +132,7 @@ def _verify(
seen_nonce=seen,
)
except lic.SignatureInvalid:
- return None, _error(400, "invalid_signature", "Signatur der Anfrage ist ungueltig.")
+ return None, _error(400, "invalid_signature", "Signatur der Anfrage ist ungültig.")
except lic.ReplayDetected as exc:
return None, _error(409, "replay", str(exc))
except lic.ProtocolViolation as exc:
@@ -150,7 +150,7 @@ def process_activate(
) -> Outcome:
row, error = _verify(conn, request, expected_action="activate")
if error is not None or row is None:
- return error or _error(400, "bad_request", "Ungueltige Anfrage.")
+ return error or _error(400, "bad_request", "Ungültige Anfrage.")
fingerprint = str(request["fingerprint"])
hostname = str(request.get("hostname") or "")
@@ -202,7 +202,7 @@ def process_activate(
target=str(row["license_id"]),
detail=(
f"Fingerprint {fingerprint[:16]}..., Host {hostname or 'unbekannt'}"
- + (f", verdraengt {len(superseded)} Lizenz(en)" if superseded else "")
+ + (f", verdrängt {len(superseded)} Lizenz(en)" if superseded else "")
),
severity="notice",
)
@@ -211,7 +211,7 @@ def process_activate(
request,
master_private_key=master_private_key,
status="ok",
- detail="Aktivierung bestaetigt.",
+ detail="Aktivierung bestätigt.",
)
@@ -220,7 +220,7 @@ def process_deactivate(
) -> Outcome:
row, error = _verify(conn, request, expected_action="deactivate")
if error is not None or row is None:
- return error or _error(400, "bad_request", "Ungueltige Anfrage.")
+ return error or _error(400, "bad_request", "Ungültige Anfrage.")
fingerprint = str(request["fingerprint"])
if str(row["status"]) != "active" or str(row["fingerprint"] or "") != fingerprint:
@@ -249,7 +249,7 @@ def process_deactivate(
request,
master_private_key=master_private_key,
status="ok",
- detail="Deaktivierung bestaetigt. Die Lizenz kann auf einer anderen Maschine aktiviert werden.",
+ detail="Deaktivierung bestätigt. Die Lizenz kann auf einer anderen Maschine aktiviert werden.",
)
@@ -258,7 +258,7 @@ def process_heartbeat(
) -> Outcome:
row, error = _verify(conn, request, expected_action="heartbeat")
if error is not None or row is None:
- return error or _error(400, "bad_request", "Ungueltige Anfrage.")
+ return error or _error(400, "bad_request", "Ungültige Anfrage.")
fingerprint = str(request["fingerprint"])
hostname = str(request.get("hostname") or "")
@@ -306,7 +306,7 @@ def process_heartbeat(
)
else:
logger.warning(
- "Bericht von Lizenz %s verworfen -- Rolle ist %s, nicht license_server",
+ "Bericht von Lizenz %s verworfen | Rolle ist %s, nicht license_server",
row["license_id"],
row["role"],
)
@@ -322,7 +322,7 @@ def process_heartbeat(
request,
master_private_key=master_private_key,
status="ok",
- detail="Verbindung bestaetigt.",
+ detail="Verbindung bestätigt.",
license_update=update,
)
diff --git a/apps/tesm-license/src/tesm_license/services/issuance.py b/apps/tesm-license/src/tesm_license/services/issuance.py
index baf3b1e..955004f 100644
--- a/apps/tesm-license/src/tesm_license/services/issuance.py
+++ b/apps/tesm-license/src/tesm_license/services/issuance.py
@@ -48,7 +48,7 @@ def validate_email(value: str, *, required: bool) -> str:
# sollen weiter bearbeitbar bleiben, statt ausgesperrt zu werden.
return ""
if not EMAIL_RE.match(text):
- raise IssuanceError(f"{text!r} ist keine gueltige E-Mail-Adresse.")
+ raise IssuanceError(f"{text!r} ist keine gültige E-Mail-Adresse.")
return text
@@ -137,7 +137,7 @@ def delete_customer(conn: sqlite3.Connection, customer_id: int) -> None:
)
if int(tickets or 0):
raise IssuanceError(
- f"An diesem Kunden haengen noch {tickets} Ticket(s). Bitte zuerst entfernen."
+ f"An diesem Kunden hängen noch {tickets} Ticket(s). Bitte zuerst entfernen."
)
conn.execute("DELETE FROM license_customers WHERE id=?", (customer_id,))
@@ -224,7 +224,7 @@ def create_ticket(
raise IssuanceError("Unbekannter Lizenztyp.")
if role not in lic.LICENSE_ROLES or role == "master":
# "master" entsteht ausschliesslich lokal per Bootstrap-Skript.
- raise IssuanceError("Unzulaessige Rolle.")
+ raise IssuanceError("Unzulässige Rolle.")
# Trial + unbefristet ist ein sinnloser Zustand: eine permanente Lizenz ohne
# Module. Diese Regel wird an drei Stellen geprueft (hier, beim Bearbeiten
@@ -290,7 +290,7 @@ def update_ticket(
if license_type != str(ticket["type"]):
changes.append(f"Typ: {ticket['type']} -> {license_type}")
if new_modules != str(ticket["modules"]):
- changes.append("Module geaendert")
+ changes.append("Module geändert")
if int(valid_days) != int(ticket["valid_days"]):
changes.append(f"Laufzeit: {ticket['valid_days']} -> {valid_days} Tage")
if bool(lifetime) != bool(ticket["lifetime"]):
@@ -428,7 +428,7 @@ def issue_from_ticket(
role = str(ticket["role"])
if as_trial and role != "customer":
raise IssuanceError(
- "Eine Testlizenz gibt es nur fuer Kundeninstanzen, nicht fuer Lizenzserver."
+ "Eine Testlizenz gibt es nur für Kundeninstanzen, nicht für Lizenzserver."
)
if as_trial:
@@ -452,9 +452,9 @@ def issue_from_ticket(
# hier, an der einzigen Stelle, durch die jede Ausstellung laeuft.
if not str(master_endpoint or "").strip():
raise IssuanceError(
- "Es ist kein oeffentlicher Endpunkt hinterlegt (Einstellungen -> Anbieter). "
- "Ohne ihn koennte sich diese Lizenz nie online aktivieren -- der Endpunkt "
- "wird in die Lizenz signiert und laesst sich spaeter nicht nachtragen."
+ "Es ist kein öffentlicher Endpunkt hinterlegt (Einstellungen -> Anbieter). "
+ "Ohne ihn könnte sich diese Lizenz nie online aktivieren – der Endpunkt "
+ "wird in die Lizenz signiert und lässt sich später nicht nachtragen."
)
customer = get_customer(conn, int(ticket["customer_id"]))
@@ -550,7 +550,7 @@ def delivery_bundle(conn: sqlite3.Connection, license_id: str, *, keystore: Any)
bundle["client_key"] = keystore.decrypt(encrypted, aad=client_key_aad(license_id))
except Exception as exc: # noqa: BLE001
raise IssuanceError(
- "Der Client-Schluessel dieser Lizenz konnte nicht entschluesselt werden. "
+ "Der Client-Schlüssel dieser Lizenz konnte nicht entschlüsselt werden. "
"Bitte eine neue Lizenz ausstellen."
) from exc
return bundle
@@ -580,7 +580,7 @@ def delete_license(conn: sqlite3.Connection, license_id: str) -> None:
raise IssuanceError("Diese Lizenz existiert nicht.")
if str(row["status"]) not in {"revoked", "issued"}:
raise IssuanceError(
- "Eine aktive Lizenz kann nicht geloescht werden. Bitte zuerst widerrufen."
+ "Eine aktive Lizenz kann nicht gelöscht werden. Bitte zuerst widerrufen."
)
conn.execute("DELETE FROM licenses WHERE license_id=?", (license_id,))
diff --git a/apps/tesm-license/src/tesm_license/services/legacy_import.py b/apps/tesm-license/src/tesm_license/services/legacy_import.py
index 585d7e3..50d2a5f 100644
--- a/apps/tesm-license/src/tesm_license/services/legacy_import.py
+++ b/apps/tesm-license/src/tesm_license/services/legacy_import.py
@@ -61,7 +61,7 @@ class Report:
def as_text(self) -> str:
lines = [
- f"Signaturschluessel uebernommen: {'ja' if self.key_adopted else 'nein'}"
+ f"Signaturschlüssel übernommen: {'ja' if self.key_adopted else 'nein'}"
+ (f" ({self.key_id})" if self.key_id else ""),
f"Kunden : {self.customers}",
f"Tickets : {self.tickets}",
@@ -69,7 +69,7 @@ class Report:
]
if self.reissued:
lines.append("")
- lines.append("Neu signiert -- diese Kunden brauchen eine neue Lizenzdatei:")
+ lines.append("Neu signiert – diese Kunden brauchen eine neue Lizenzdatei:")
lines.extend(f" * {entry}" for entry in self.reissued)
if self.skipped:
lines.append("")
@@ -95,7 +95,7 @@ def open_legacy(root: Path) -> sqlite3.Connection:
"""Oeffnet die alte Datenbank **nur lesend**."""
path = Path(root) / "sqlite.db"
if not path.is_file():
- raise LegacyImportError(f"Unter {path} liegt keine Datenbank der Vorgaengerfassung.")
+ raise LegacyImportError(f"Unter {path} liegt keine Datenbank der Vorgängerfassung.")
try:
conn = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
except sqlite3.Error as exc:
@@ -107,7 +107,7 @@ def open_legacy(root: Path) -> sqlite3.Connection:
if missing:
conn.close()
raise LegacyImportError(
- f"Das sieht nicht nach einem Lizenzserver aus -- es fehlen: {', '.join(sorted(missing))}"
+ f"Das sieht nicht nach einem Lizenzserver aus – es fehlen: {', '.join(sorted(missing))}"
)
return conn
@@ -123,7 +123,7 @@ def _standard_b64(wert: str, feld: str) -> str:
"""
roh = wert.strip()
if not roh:
- raise LegacyImportError(f"Im alten Signaturschluessel fehlt {feld!r}.")
+ raise LegacyImportError(f"Im alten Signaturschlüssel fehlt {feld!r}.")
# Fehlende Polsterung ergaenzen -- der Vorgaenger schrieb sie mit, andere
# Werkzeuge lassen sie weg.
gepolstert = roh + "=" * (-len(roh) % 4)
@@ -138,29 +138,29 @@ def _standard_b64(wert: str, feld: str) -> str:
continue
if len(rohbytes) != 32:
raise LegacyImportError(
- f"{feld} im alten Signaturschluessel ist {len(rohbytes)} Byte lang, "
+ f"{feld} im alten Signaturschlüssel ist {len(rohbytes)} Byte lang, "
"erwartet werden 32 (Ed25519)."
)
return base64.b64encode(rohbytes).decode("ascii")
- raise LegacyImportError(f"{feld} im alten Signaturschluessel ist kein Base64.")
+ raise LegacyImportError(f"{feld} im alten Signaturschlüssel ist kein Base64.")
def read_legacy_key(root: Path) -> tuple[str, str]:
"""Liest den alten Signaturschluessel. Rueckgabe: ``(privat, oeffentlich)``."""
path = Path(root) / "master_signing_key.json"
if not path.is_file():
- raise LegacyImportError(f"Der alte Signaturschluessel {path} fehlt.")
+ raise LegacyImportError(f"Der alte Signaturschlüssel {path} fehlt.")
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
- raise LegacyImportError(f"Der alte Signaturschluessel ist unlesbar: {exc}") from exc
+ raise LegacyImportError(f"Der alte Signaturschlüssel ist unlesbar: {exc}") from exc
# Die Vorgaengerfassung nannte die Felder "private"/"public" und kodierte
# sie URL-sicher.
private = str(data.get("private") or data.get("private_key") or "")
public = str(data.get("public") or data.get("public_key") or "")
if not private or not public:
- raise LegacyImportError("Der alte Signaturschluessel ist unvollstaendig.")
+ raise LegacyImportError("Der alte Signaturschlüssel ist unvollständig.")
private = _standard_b64(private, "private")
public = _standard_b64(public, "public")
# Alles, was aus der Protokollschicht kommt, wird hier zu einer Meldung im
@@ -170,11 +170,11 @@ def read_legacy_key(root: Path) -> tuple[str, str]:
try:
passt = licsrv.public_from_private(private) == public
except (ValueError, TypeError) as exc:
- raise LegacyImportError(f"Der alte Signaturschluessel ist unbrauchbar: {exc}") from exc
+ raise LegacyImportError(f"Der alte Signaturschlüssel ist unbrauchbar: {exc}") from exc
if not passt:
raise LegacyImportError(
- "Im alten Signaturschluessel passen privater und oeffentlicher Teil nicht "
- "zueinander. Die Datei ist beschaedigt."
+ "Im alten Signaturschlüssel passen privater und öffentlicher Teil nicht "
+ "zueinander. Die Datei ist beschädigt."
)
return private, public
@@ -190,16 +190,16 @@ def adopt_key(private: str, public: str, target: Path) -> str:
if Path(target).is_file():
raise LegacyImportError(
- f"Unter {target} liegt bereits ein Signaturschluessel. Er wird nicht "
- "ueberschrieben -- das wuerde jede damit ausgestellte Lizenz unbrauchbar "
- "machen. Fuer eine Uebernahme bitte eine frische Installation verwenden."
+ f"Unter {target} liegt bereits ein Signaturschlüssel. Er wird nicht "
+ "überschrieben – das würde jede damit ausgestellte Lizenz unbrauchbar "
+ "machen. Für eine Übernahme bitte eine frische Installation verwenden."
)
payload = {
"private_key": private,
"public_key": public,
"key_id": lic.key_id(public),
"created_at": _now(),
- "origin": "Uebernommen aus der Vorgaengerfassung.",
+ "origin": "Übernommen aus der Vorgängerfassung.",
}
write_private_file(Path(target), json.dumps(payload, indent=2, sort_keys=True) + "\n")
return lic.key_id(public)
@@ -309,7 +309,7 @@ def import_all(
)
if existing is not None:
customer_map[int(row["id"])] = int(existing["id"])
- report.warnings.append(f"Kunde {name!r} war schon vorhanden -- nicht doppelt angelegt.")
+ report.warnings.append(f"Kunde {name!r} war schon vorhanden – nicht doppelt angelegt.")
continue
new_id = issuance.create_customer(
conn,
@@ -331,7 +331,7 @@ def import_all(
for row in legacy.execute("SELECT * FROM license_tickets ORDER BY created_at"):
customer_id = customer_map.get(int(row["customer_id"] or 0))
if customer_id is None:
- report.skipped.append(f"Ticket {str(row['id'])[:12]} -- Kunde nicht uebernommen")
+ report.skipped.append(f"Ticket {str(row['id'])[:12]} -- Kunde nicht übernommen")
continue
if Database.one(conn, "SELECT id FROM license_tickets WHERE id=?", (row["id"],)):
continue
@@ -348,7 +348,7 @@ def import_all(
int(row["lifetime"] or 0),
str(row["status"] or "open"),
str(row["role"] or "customer"),
- "Aus der Vorgaengerfassung uebernommen.",
+ "Aus der Vorgängerfassung übernommen.",
str(row["created_by"] or actor),
str(row["created_at"] or _now()),
_now(),
@@ -361,7 +361,7 @@ def import_all(
license_id = str(row["license_id"] or "")
customer_id = customer_map.get(int(row["customer_id"] or 0))
if not license_id or customer_id is None:
- report.skipped.append(f"Lizenz {license_id[:12] or '?'} -- Kunde nicht uebernommen")
+ report.skipped.append(f"Lizenz {license_id[:12] or '?'} -- Kunde nicht übernommen")
continue
if Database.one(conn, "SELECT id FROM licenses WHERE license_id=?", (license_id,)):
continue
@@ -371,7 +371,7 @@ def import_all(
modules = _modules(row["modules"])
if not reissue:
- report.skipped.append(f"{customer['name']} / {license_id[:12]} -- nur gezaehlt")
+ report.skipped.append(f"{customer['name']} / {license_id[:12]} -- nur gezählt")
report.licenses += 1
continue
@@ -383,7 +383,7 @@ def import_all(
if not lifetime and valid_days <= 0:
report.skipped.append(
f"{customer['name']} / {license_id[:12]} -- abgelaufen am "
- f"{str(row['expires_at'])[:10]}, nicht uebernommen"
+ f"{str(row['expires_at'])[:10]}, nicht übernommen"
)
continue
@@ -444,7 +444,7 @@ def import_all(
if report.reissued:
report.warnings.append(
"Die neu signierten Lizenzen sind noch nicht aktiviert: die Bindung an eine "
- "Maschine gilt nur fuer die alte Datei. Nach dem Einspielen aktiviert der "
+ "Maschine gilt nur für die alte Datei. Nach dem Einspielen aktiviert der "
"Kunde erneut."
)
return report
diff --git a/apps/tesm-license/src/tesm_license/services/mail.py b/apps/tesm-license/src/tesm_license/services/mail.py
index 4b4f65b..7233815 100644
--- a/apps/tesm-license/src/tesm_license/services/mail.py
+++ b/apps/tesm-license/src/tesm_license/services/mail.py
@@ -92,7 +92,7 @@ def _secret(conn: sqlite3.Connection) -> str:
return core().keystore.decrypt(str(value), aad=AAD_GRAPH_SECRET)
except Exception as exc: # noqa: BLE001
raise MailError(
- "Das Client-Secret konnte nicht entschluesselt werden -- bitte neu eintragen."
+ "Das Client-Secret konnte nicht entschlüsselt werden – bitte neu eintragen."
) from exc
@@ -162,7 +162,7 @@ def fetch_token(conn: sqlite3.Connection) -> str:
def test_connection(conn: sqlite3.Connection) -> dict[str, Any]:
"""Holt nur ein Token -- verschickt bewusst keine echte Nachricht."""
if not is_enabled(conn):
- return {"ok": False, "message": "Der E-Mail-Versand ist nicht vollstaendig konfiguriert."}
+ return {"ok": False, "message": "Der E-Mail-Versand ist nicht vollständig konfiguriert."}
try:
fetch_token(conn)
except MailError as exc:
diff --git a/apps/tesm-license/src/tesm_license/services/master_key.py b/apps/tesm-license/src/tesm_license/services/master_key.py
index 8ff9402..c3cc7f0 100644
--- a/apps/tesm-license/src/tesm_license/services/master_key.py
+++ b/apps/tesm-license/src/tesm_license/services/master_key.py
@@ -48,22 +48,22 @@ class MasterKey:
def load(path: Path) -> MasterKey:
if not path.is_file():
raise MasterKeyError(
- f"Der Signaturschluessel {path} fehlt. Ohne ihn kann keine Lizenz ausgestellt "
+ f"Der Signaturschlüssel {path} fehlt. Ohne ihn kann keine Lizenz ausgestellt "
"werden. Erzeugen Sie ihn mit 'tesm-license-admin init-key'."
)
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
- raise MasterKeyError(f"Der Signaturschluessel ist unlesbar: {exc}") from exc
+ raise MasterKeyError(f"Der Signaturschlüssel ist unlesbar: {exc}") from exc
private = str(data.get("private_key") or "")
public = str(data.get("public_key") or "")
if not private or not public:
- raise MasterKeyError("Der Signaturschluessel ist unvollstaendig.")
+ raise MasterKeyError("Der Signaturschlüssel ist unvollständig.")
if licsrv.public_from_private(private) != public:
raise MasterKeyError(
- "Der oeffentliche Teil des Signaturschluessels passt nicht zum privaten. "
- "Die Datei ist beschaedigt oder wurde manipuliert."
+ "Der öffentliche Teil des Signaturschlüssels passt nicht zum privaten. "
+ "Die Datei ist beschädigt oder wurde manipuliert."
)
return MasterKey(
private_key=private, public_key=public, created_at=str(data.get("created_at") or "")
@@ -87,7 +87,7 @@ def ensure(path: Path) -> MasterKey:
}
write_private_file(path, json.dumps(payload, indent=2, sort_keys=True) + "\n")
logger.warning(
- "Neuer Signaturschluessel erzeugt (%s). Diese Datei ist unwiederbringlich -- "
+ "Neuer Signaturschlüssel erzeugt (%s). Diese Datei ist unwiederbringlich – "
"bitte sofort gesichert ablegen: %s",
lic.key_id(public),
path,
@@ -98,7 +98,7 @@ def ensure(path: Path) -> MasterKey:
def health(path: Path) -> dict[str, object]:
"""Zustand fuer die Diagnoseseite."""
if not path.is_file():
- return {"present": False, "problems": ["Signaturschluessel fehlt"]}
+ return {"present": False, "problems": ["Signaturschlüssel fehlt"]}
problems = check_private_file(path)
try:
key = load(path)
diff --git a/apps/tesm-license/src/tesm_license/services/transfer.py b/apps/tesm-license/src/tesm_license/services/transfer.py
index 1ffd0ea..e28c9b3 100644
--- a/apps/tesm-license/src/tesm_license/services/transfer.py
+++ b/apps/tesm-license/src/tesm_license/services/transfer.py
@@ -41,11 +41,11 @@ APP_TAG = "tesm-license"
CATEGORIES: tuple[Category, ...] = (
Category("customers", "Kunden", "Stammdaten der Kundeninstanzen"),
- Category("tickets", "Tickets", "Anspruechte inklusive Self-Service-Schluessel", sensitive=True),
+ Category("tickets", "Tickets", "Ansprüche inklusive Self-Service-Schlüssel", sensitive=True),
Category(
"licenses",
"Lizenzen",
- "Ausgestellte Lizenzen mit signiertem Dokument und verschluesseltem Client-Schluessel",
+ "Ausgestellte Lizenzen mit signiertem Dokument und verschlüsseltem Client-Schlüssel",
sensitive=True,
),
Category("users", "Benutzer", "Lokale Konten mit Passwort-Hash", admin_only=True),
diff --git a/apps/tesm-license/src/tesm_license/settings_spec.py b/apps/tesm-license/src/tesm_license/settings_spec.py
index f442e02..f326e99 100644
--- a/apps/tesm-license/src/tesm_license/settings_spec.py
+++ b/apps/tesm-license/src/tesm_license/settings_spec.py
@@ -25,8 +25,8 @@ VENDOR_SETTINGS: tuple[SettingSpec, ...] = (
"master_endpoint",
"",
"str",
- "Oeffentlicher Endpunkt dieses Lizenzservers",
- hint="Basis-URL, unter der Kundeninstanzen diesen Server erreichen -- z. B. "
+ "Öffentlicher Endpunkt dieses Lizenzservers",
+ hint="Basis-URL, unter der Kundeninstanzen diesen Server erreichen – z. B. "
"https://lizenz.firma.de. Wird in jede Lizenz eingebettet und fuer alle "
"kundengerichteten Links verwendet.",
section="vendor",
@@ -38,8 +38,8 @@ PORTAL_SETTINGS: tuple[SettingSpec, ...] = (
"portal_link_minutes",
30,
"int",
- "Gueltigkeit des Zugangslinks (Minuten)",
- hint="Der Link ist zusaetzlich nur einmal verwendbar.",
+ "Gültigkeit des Zugangslinks (Minuten)",
+ hint="Der Link ist zusätzlich nur einmal verwendbar.",
section="portal",
minimum=5,
maximum=1440,
@@ -48,9 +48,9 @@ PORTAL_SETTINGS: tuple[SettingSpec, ...] = (
"portal_session_minutes",
60,
"int",
- "Gueltigkeit der Portalsitzung (Minuten)",
- hint="Zweites, unabhaengiges Zeitfenster: eines fuer das Anklicken des Links, "
- "eines fuer die daraus entstehende Sitzung.",
+ "Gültigkeit der Portalsitzung (Minuten)",
+ hint="Zweites, unabhängiges Zeitfenster: eines für das Anklicken des Links, "
+ "eines für die daraus entstehende Sitzung.",
section="portal",
minimum=5,
maximum=1440,
@@ -64,7 +64,7 @@ PORTAL_SETTINGS: tuple[SettingSpec, ...] = (
"bool",
"Self-Service-Link je Ticket aktiv",
hint="Erlaubt dem Kunden, seine Lizenz selbst herunterzuladen, zu widerrufen und "
- "neu auszustellen -- ohne Anmeldung, allein ueber den unratbaren Ticket-Link.",
+ "neu auszustellen – ohne Anmeldung, allein über den unratbaren Ticket-Link.",
section="portal",
),
)
@@ -83,8 +83,8 @@ DEFAULT_TICKET_SETTINGS: tuple[SettingSpec, ...] = (
"allow_customer_revoke",
True,
"bool",
- "Kunden duerfen ihre Lizenz selbst widerrufen",
- hint="Notwendig fuer einen Systemwechsel ohne Support-Anruf.",
+ "Kunden dürfen ihre Lizenz selbst widerrufen",
+ hint="Notwendig für einen Systemwechsel ohne Support-Anruf.",
section="licensing",
),
)
diff --git a/apps/tesm-license/src/tesm_license/templates/license/license_detail.html b/apps/tesm-license/src/tesm_license/templates/license/license_detail.html
index 92e2cd3..8e47fb1 100644
--- a/apps/tesm-license/src/tesm_license/templates/license/license_detail.html
+++ b/apps/tesm-license/src/tesm_license/templates/license/license_detail.html
@@ -170,7 +170,7 @@
- Das Ticket bleibt offen -- eine neue Lizenz kann sofort ausgestellt werden. Genau so + Das Ticket bleibt offen – eine neue Lizenz kann sofort ausgestellt werden. Genau so läuft ein Systemwechsel.
{% endif %} diff --git a/apps/tesm-license/src/tesm_license/templates/license/portal_request.html b/apps/tesm-license/src/tesm_license/templates/license/portal_request.html index 6515f60..d8927ba 100644 --- a/apps/tesm-license/src/tesm_license/templates/license/portal_request.html +++ b/apps/tesm-license/src/tesm_license/templates/license/portal_request.html @@ -22,7 +22,7 @@- Aus Datenschutzgründen bekommen Sie immer dieselbe Rückmeldung -- unabhängig davon, ob die + Aus Datenschutzgründen bekommen Sie immer dieselbe Rückmeldung – unabhängig davon, ob die Adresse bei uns bekannt ist.
{% endblock %} diff --git a/apps/tesm-license/src/tesm_license/templates/license/selfservice_offline.html b/apps/tesm-license/src/tesm_license/templates/license/selfservice_offline.html index 1f85c1c..76c88a3 100644 --- a/apps/tesm-license/src/tesm_license/templates/license/selfservice_offline.html +++ b/apps/tesm-license/src/tesm_license/templates/license/selfservice_offline.html @@ -15,7 +15,7 @@ {% if response_code %}{{ response_code }}
- Tragen Sie ihn in TESM unter Verwaltung -- Lizenz bei + Tragen Sie ihn in TESM unter Verwaltung → Lizenz bei „Antwortcode des Lizenzservers“ ein. {% if summary.hostname %} Die Anfrage kam von {{ summary.hostname }}. diff --git a/apps/tesm-license/src/tesm_license/templates/license/settings_vendor.html b/apps/tesm-license/src/tesm_license/templates/license/settings_vendor.html index 9a9deb2..1087fe1 100644 --- a/apps/tesm-license/src/tesm_license/templates/license/settings_vendor.html +++ b/apps/tesm-license/src/tesm_license/templates/license/settings_vendor.html @@ -45,7 +45,7 @@ welche Basis-URL in kundengerichteten Links steht.', 'info') }} - Ein zu grosses Logo wird abgelehnt, die übrigen Angaben werden trotzdem gespeichert. + Ein zu großes Logo wird abgelehnt, die übrigen Angaben werden trotzdem gespeichert.
Die Angaben werden zum Zeitpunkt der Ausstellung in die Lizenz kopiert und signiert. Eine - Änderung hier wirkt auf neue Lizenzen -- bestehende behalten ihren Stand, bis eine neue + Änderung hier wirkt auf neue Lizenzen – bestehende behalten ihren Stand, bis eine neue Revision signiert wird.
diff --git a/apps/tesm-license/src/tesm_license/templates/license/ticket_detail.html b/apps/tesm-license/src/tesm_license/templates/license/ticket_detail.html index 04cfec8..1125cc1 100644 --- a/apps/tesm-license/src/tesm_license/templates/license/ticket_detail.html +++ b/apps/tesm-license/src/tesm_license/templates/license/ticket_detail.html @@ -33,7 +33,7 @@Damit kann der Kunde seine Lizenz herunterladen, bei einem Systemwechsel freigeben und - neu ausstellen. Der Link enthält 192 Bit Zufall und ist nicht ratbar -- behandeln Sie + neu ausstellen. Der Link enthält 192 Bit Zufall und ist nicht ratbar – behandeln Sie ihn wie ein Passwort.
diff --git a/apps/tesm-license/src/tesm_license/templates/license/ticket_new.html b/apps/tesm-license/src/tesm_license/templates/license/ticket_new.html index 6f74d1a..8e7914d 100644 --- a/apps/tesm-license/src/tesm_license/templates/license/ticket_new.html +++ b/apps/tesm-license/src/tesm_license/templates/license/ticket_new.html @@ -32,7 +32,7 @@ {% endfor %} - Der Kunde ist nach dem Anlegen unveränderlich -- der Self-Service-Link ist an ihn + Der Kunde ist nach dem Anlegen unveränderlich – der Self-Service-Link ist an ihn gebunden. @@ -77,7 +77,7 @@Der Self-Service-Link des Tickets erlaubt dem Kunden, seine Lizenz selbst - herunterzuladen und bei einem Systemwechsel freizugeben -- ohne Support-Anruf. + herunterzuladen und bei einem Systemwechsel freizugeben – ohne Support-Anruf.