fix: agent-08 Kiosk-Härtung + 24h-Zeiteintrag-Bug
- fix: worked_minutes nutzt jetzt Sekunden statt Minuten für Overnight-Vergleich (end < start statt end <= start) – verhindert 24h-Anzeige bei Schnell-Stempel in derselben Minute (z.B. 23:34:46 → 23:34:48) - fix: _check_arbzg() gleicher Sec-basierter Fix - fix: KioskDeviceStatus Enum values_callable → kiosk list crasht nicht mehr - feat: kiosk rotate-key CLI-Kommando (Status→pending, Re-Enrollment) - feat: Kiosk-Settings in CompanyOut/CompanyUpdate Schema (require_approval, track_current_user, heartbeat_interval_sec) - feat: Kiosk-Terminal-Einstellungsblock in CompanySettingsPage (🖥️) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -451,6 +451,84 @@ def kiosk_revoke(
|
||||
)
|
||||
|
||||
|
||||
# ── Subcommand: kiosk rotate-key ─────────────────────────────────────────────
|
||||
|
||||
@kiosk_app.command("rotate-key")
|
||||
def kiosk_rotate_key(
|
||||
device_id: str = typer.Argument(..., help="UUID des Kiosk-Geräts"),
|
||||
pubkey: Optional[Path] = typer.Option(None, "--pubkey", help="Pfad zur neuen Public-Key-Datei (optional)"),
|
||||
yes: bool = typer.Option(False, "--yes", "-y", help="Ohne Bestätigung fortfahren"),
|
||||
):
|
||||
"""Ed25519-Schlüssel eines Kiosk-Geräts rotieren (Status → pending, Re-Enrollment erforderlich)."""
|
||||
|
||||
# Neuen Public Key einlesen falls angegeben
|
||||
new_pubkey_str: Optional[str] = None
|
||||
if pubkey is not None:
|
||||
if not pubkey.exists():
|
||||
err_console.print(f"[bold red]Fehler:[/bold red] Public-Key-Datei nicht gefunden: {pubkey}")
|
||||
raise typer.Exit(1)
|
||||
new_pubkey_str = pubkey.read_text().strip()
|
||||
if not new_pubkey_str:
|
||||
err_console.print("[bold red]Fehler:[/bold red] Public-Key-Datei ist leer.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Gerät vorab laden für Bestätigungsdialog
|
||||
if not yes:
|
||||
async def _peek():
|
||||
session = await _open_session()
|
||||
try:
|
||||
return await _find_device(session, device_id)
|
||||
finally:
|
||||
await session.close()
|
||||
device_peek = _run(_peek())
|
||||
console.print(
|
||||
f"[yellow]Achtung:[/yellow] Gerät [bold]{device_peek.name}[/bold] "
|
||||
f"({str(device_peek.id)[:8]}…) wird auf [yellow]pending[/yellow] gesetzt.\n"
|
||||
"Der aktuelle Schlüssel wird gelöscht. Re-Enrollment ist erforderlich."
|
||||
)
|
||||
confirm = typer.confirm("Schlüssel wirklich rotieren?")
|
||||
if not confirm:
|
||||
console.print("[dim]Abgebrochen.[/dim]")
|
||||
raise typer.Exit(0)
|
||||
|
||||
async def _rotate():
|
||||
session = await _open_session()
|
||||
try:
|
||||
device = await _find_device(session, device_id)
|
||||
device.public_key = new_pubkey_str
|
||||
device.enrollment_token_hash = None
|
||||
device.enrollment_expires_at = None
|
||||
device.status = KioskDeviceStatus.PENDING
|
||||
await session.commit()
|
||||
await session.refresh(device)
|
||||
return device
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
device = _run(_rotate())
|
||||
|
||||
console.print()
|
||||
if new_pubkey_str:
|
||||
fingerprint = _pubkey_fingerprint(new_pubkey_str)
|
||||
console.print(
|
||||
f"[bold green]✓[/bold green] Schlüssel für Gerät [bold]{device.name}[/bold] "
|
||||
f"({str(device.id)[:8]}…) rotiert.\n"
|
||||
f" [bold]Neuer Fingerprint:[/bold] {fingerprint}\n"
|
||||
f" [bold]Status:[/bold] {_status_icon(device.status)}"
|
||||
)
|
||||
else:
|
||||
console.print(
|
||||
f"[bold green]✓[/bold green] Public Key für Gerät [bold]{device.name}[/bold] "
|
||||
f"({str(device.id)[:8]}…) wurde gelöscht.\n"
|
||||
f" [bold]Status:[/bold] {_status_icon(device.status)}\n"
|
||||
" [yellow]Hinweis:[/yellow] Bitte Public Key per Enrollment-Flow neu setzen."
|
||||
)
|
||||
console.print()
|
||||
|
||||
|
||||
# ── Subcommand: kiosk info ────────────────────────────────────────────────────
|
||||
|
||||
@kiosk_app.command("info")
|
||||
|
||||
Reference in New Issue
Block a user