feat(admin): hemmeligheds-drop til Claude
/admin/secrets tager imod et token eller kodeord og krypterer det med det samme med PVE-værtens offentlige X25519-nøgle (sealed box: HKDF-SHA256 + ChaCha20-Poly1305, navnet som associated data). Kun ciphertext gemmes; portalen kan ikke læse det igen. På værten henter secret-drop get <navn> det én gang og sletter kopien her. Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
This commit is contained in:
@@ -20,6 +20,7 @@ import ytdl as yt
|
||||
import mcpkeys
|
||||
import mcp_server
|
||||
import mcp_gateway
|
||||
import secretdrop
|
||||
|
||||
# Portalens egen MCP (FastMCP), monteret in-process på /mcp. json_response=True
|
||||
# => almindelig JSON (ingen SSE), så det passerer edge'en utunet. Bearer-gated.
|
||||
@@ -324,6 +325,34 @@ async def admin_invitations_delete(request: Request, pk: str):
|
||||
status_code=303)
|
||||
|
||||
|
||||
@app.get("/admin/secrets", response_class=HTMLResponse)
|
||||
async def admin_secrets(request: Request, saved: str = "", gemt: str = ""):
|
||||
user = require_admin(request)
|
||||
return templates.TemplateResponse(request, "admin_secrets.html", {
|
||||
"request": request, "user": user, "is_admin": True,
|
||||
"ventende": secretdrop.pending(), "saved": saved, "gemt": gemt})
|
||||
|
||||
|
||||
@app.post("/admin/secrets")
|
||||
async def admin_secrets_store(request: Request):
|
||||
user = require_admin(request)
|
||||
f = await request.form()
|
||||
navn = (f.get("navn") or "").strip().lower()
|
||||
fejl = secretdrop.store(navn, f.get("vaerdi") or "", f.get("note") or "",
|
||||
(user or {}).get("preferred_username") or (user or {}).get("email") or "?")
|
||||
if fejl:
|
||||
return RedirectResponse(f"/admin/secrets?saved={fejl}", status_code=303)
|
||||
# Værdien sendes aldrig tilbage; kun navnet, så siden kan bekræfte det.
|
||||
return RedirectResponse(f"/admin/secrets?gemt={navn}", status_code=303)
|
||||
|
||||
|
||||
@app.post("/admin/secrets/{navn}/slet")
|
||||
async def admin_secrets_delete(request: Request, navn: str):
|
||||
require_admin(request)
|
||||
ok = secretdrop.delete(navn)
|
||||
return RedirectResponse(f"/admin/secrets?saved={'Slettet' if ok else 'Fandtes ikke'}", status_code=303)
|
||||
|
||||
|
||||
@app.get("/admin/groups", response_class=HTMLResponse)
|
||||
async def admin_groups(request: Request, saved: str = ""):
|
||||
user = require_admin(request)
|
||||
|
||||
Reference in New Issue
Block a user