TESM 2.0.0 -- Neubau

Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert
im Zweig SONNET5 erhalten.

Aufbau: apps/tesm (Anwendung), packages/tesm-core (gemeinsamer Kern),
packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd,
privilegierter Helfer), docs, tests. Der Lizenzserver liegt in seinem eigenen
Repository; beide Repositorien bringen die gemeinsamen Pakete mit, damit sich
jedes allein installieren laesst.

Die wichtigsten Unterschiede zum Vorgaenger, jeweils an der Stelle im Code
kommentiert, an der der Fehler entstanden ist:

* Der Webprozess laeuft unprivilegiert. Alles, was Root braucht, geht ueber
  einen einzigen Helfer mit Positivlisten fuer jedes Argument.
* CSRF-Schutz ueberhaupt -- der Vorgaenger hatte keinen.
* Rechte werden serverseitig geprueft, nicht nur im Template ausgeblendet.
* Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert.
* Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft (nginx, Kea).
* Kein Hostname im Lizenz-Fingerabdruck.
* Zwei Installationen auf einem Host stoeren sich nicht (eigener SITE_KEY).
* Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 18:00:06 +02:00
co-authored by Claude Opus 5
parent a193b9d6e9
commit 7354ff352b
227 changed files with 37605 additions and 21187 deletions
@@ -0,0 +1,125 @@
"""TESM-Lizenzprotokoll -- gemeinsames Paket von TESM und TESM-Lizenzserver.
Im Vorgaengerprojekt lag dieses Modul als handkopierte, byte-identisch zu
haltende ``licensing.py`` in beiden Repos. Jede unabhaengige Aenderung an nur
einer Kopie brach die gegenseitige Signaturpruefung, ohne dass das lokal
auffiel. Hier ist es ein installierbares Paket mit eigener Version -- die
Invariante ist damit strukturell erfuellt statt durch Disziplin.
"""
from __future__ import annotations
from .canonical import (
SIG_CONTEXT_LICENSE,
SIG_CONTEXT_REQUEST,
SIG_CONTEXT_RESPONSE,
canonical_bytes,
decode_code,
encode_code,
generate_keypair,
public_from_private,
sign,
verify,
)
from .errors import (
InvalidLicenseFile,
LicensingError,
ProtocolViolation,
ReplayDetected,
SignatureInvalid,
UntrustedIssuer,
)
from .fingerprint import machine_identity, system_fingerprint
from .license import (
ALL_MODULES,
EXPIRY_WARNING_DAYS,
FORMAT_VERSION,
GRACE_PERIOD_DAYS,
HEARTBEAT_WARNING_DAYS,
LICENSE_ROLES,
LICENSE_TYPE_LABELS,
LICENSE_TYPES,
LIFETIME_EXPIRES_AT,
MODULE_LABELS,
ROLE_LABELS,
TRIAL_DEFAULT_DAYS,
LicenseStatus,
issue_license,
iso,
key_id,
license_status,
parse_iso,
resign_license,
split_bundle,
utcnow,
verify_license,
)
from .protocol import (
ACTIONS,
CLOCK_SKEW_SECONDS,
HEARTBEAT_INTERVAL_SECONDS,
MAX_REPORT_ROWS,
REQUEST_MAX_AGE_SECONDS,
RESPONSE_STATES,
build_client_request,
build_master_response,
new_nonce,
verify_client_request,
verify_master_response,
)
__version__ = "2.0.0"
__all__ = [
"ACTIONS",
"ALL_MODULES",
"CLOCK_SKEW_SECONDS",
"EXPIRY_WARNING_DAYS",
"FORMAT_VERSION",
"GRACE_PERIOD_DAYS",
"HEARTBEAT_INTERVAL_SECONDS",
"HEARTBEAT_WARNING_DAYS",
"InvalidLicenseFile",
"LICENSE_ROLES",
"LICENSE_TYPES",
"LICENSE_TYPE_LABELS",
"LIFETIME_EXPIRES_AT",
"LicenseStatus",
"LicensingError",
"MAX_REPORT_ROWS",
"MODULE_LABELS",
"ProtocolViolation",
"REQUEST_MAX_AGE_SECONDS",
"RESPONSE_STATES",
"ROLE_LABELS",
"ReplayDetected",
"SIG_CONTEXT_LICENSE",
"SIG_CONTEXT_REQUEST",
"SIG_CONTEXT_RESPONSE",
"SignatureInvalid",
"TRIAL_DEFAULT_DAYS",
"UntrustedIssuer",
"build_client_request",
"build_master_response",
"canonical_bytes",
"decode_code",
"encode_code",
"generate_keypair",
"issue_license",
"iso",
"key_id",
"license_status",
"machine_identity",
"new_nonce",
"parse_iso",
"public_from_private",
"resign_license",
"sign",
"split_bundle",
"system_fingerprint",
"utcnow",
"verify",
"verify_client_request",
"verify_license",
"verify_master_response",
]