feat(web): Seite „Schlüssel“ und Schlüsselauswahl in der Anbindung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
686542ff6f
commit
fb975f1eae
+67
-1
@@ -154,7 +154,16 @@ def test_security_headers(client: TestClient) -> None:
|
||||
|
||||
|
||||
def test_no_inline_scripts_or_styles(client: TestClient) -> None:
|
||||
for url in ("/", "/mounts", "/mounts/new", "/rules", "/rules/new", "/log", "/settings"):
|
||||
for url in (
|
||||
"/",
|
||||
"/mounts",
|
||||
"/mounts/new",
|
||||
"/keys",
|
||||
"/rules",
|
||||
"/rules/new",
|
||||
"/log",
|
||||
"/settings",
|
||||
):
|
||||
html = client.get(url).text
|
||||
assert "style=" not in html, url
|
||||
assert not re.search(r"<script(?![^>]*\bsrc=)", html), url
|
||||
@@ -555,3 +564,60 @@ def test_export_excludes_secrets_and_import_roundtrip(
|
||||
files={"file": ("export.json", text, "application/json")},
|
||||
)
|
||||
assert "Übersprungen" in c2.get("/settings").text
|
||||
|
||||
|
||||
# -- Schlüsselverwaltung -------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_keys_page_generate_import_select_delete(client: TestClient, state: NetMover) -> None:
|
||||
from netmover.crypto import generate_ssh_keypair
|
||||
|
||||
r = post(client, "/keys/generate", {"name": "paperless-lxc"})
|
||||
assert r.status_code == 303
|
||||
private, public = generate_ssh_keypair()
|
||||
r = post(client, "/keys/import", {"name": "alt", "private_key": private, "passphrase": ""})
|
||||
assert r.status_code == 303
|
||||
page = client.get("/keys").text
|
||||
assert "paperless-lxc" in page and "alt" in page
|
||||
assert public.split()[1] in page # öffentlicher Teil sichtbar
|
||||
for line in private.splitlines()[1:-1]: # keine Zeile des privaten Schlüssels
|
||||
assert line not in page
|
||||
assert "restrict,command="/usr/lib/openssh/sftp-server"" in page
|
||||
|
||||
with state.session_factory() as s:
|
||||
key_id = s.scalar(select(Credential.id).where(Credential.name == "alt"))
|
||||
form = client.get("/mounts/new").text
|
||||
assert f'value="{key_id}"' in form and "neuer Schlüssel nur für diese Anbindung" in form
|
||||
|
||||
r = post(
|
||||
client,
|
||||
"/mounts/new",
|
||||
{
|
||||
"name": "paperless",
|
||||
"type": "sftp",
|
||||
"server": "10.0.0.5",
|
||||
"share": "/consume",
|
||||
"port": "22",
|
||||
"auth": "ssh_key",
|
||||
"key_id": str(key_id),
|
||||
"username": "root",
|
||||
"enabled": "on",
|
||||
},
|
||||
)
|
||||
assert r.status_code == 303, r.text
|
||||
mounts = client.get("/mounts").text
|
||||
assert "gespeichertem Schlüssel" in mounts and "„alt“" in mounts
|
||||
edit = client.get(f"/mounts/{_mount_id(state, 'paperless')}/edit").text
|
||||
assert f'<option value="{key_id}" selected>' in edit and 'value="root"' in edit
|
||||
|
||||
post(client, f"/keys/{key_id}/delete")
|
||||
assert "noch benutzt" in client.get("/keys").text
|
||||
post(client, f"/mounts/{_mount_id(state, 'paperless')}/delete")
|
||||
post(client, f"/keys/{key_id}/delete")
|
||||
page = client.get("/keys").text
|
||||
assert "gelöscht" in page and ">alt<" not in page
|
||||
|
||||
|
||||
def test_key_import_error_is_shown(client: TestClient) -> None:
|
||||
post(client, "/keys/import", {"name": "x", "private_key": "kein schlüssel", "passphrase": ""})
|
||||
assert "Kein lesbarer privater Schlüssel" in client.get("/keys").text
|
||||
|
||||
Reference in New Issue
Block a user