umbau 1.0

This commit is contained in:
2026-09-02 20:30:44 +02:00
parent afe6719f51
commit 5c95b21be7
77 changed files with 10733 additions and 1914 deletions

View File

@ -69,6 +69,52 @@ async def _setup_hostgroup_and_host(conn, *, group_name="p9-group", hostname="p9
return hg_id, cursor.lastrowid
async def _grant_group_role(conn, *, user_id: int, host_group_id: int, role_names, group_name: str) -> int:
"""Teil D Schritt 4/5: Rechte gibt es nur noch ueber Benutzergruppen --
ersetzt das fruehere direkte INSERT INTO user_hostgroup_roles bzw. den
seit Schritt 5 mit HTTP 410 antwortenden Endpunkt POST
/admin/roles/grant. Legt eine neue Gruppe mit user_id als einzigem
Mitglied an und vergibt die angegebenen Rollen auf host_group_id.
Gibt die neue user_group_id zurueck (fuer eine anschliessende
Achse-B-Freigabe, siehe _grant_group_credential)."""
cursor = await conn.execute("INSERT INTO user_groups (name) VALUES (?)", (group_name,))
group_id = cursor.lastrowid
await conn.execute(
"INSERT INTO user_group_members (user_group_id, user_id) VALUES (?, ?)", (group_id, user_id)
)
for role_name in role_names:
role_row = await (await conn.execute("SELECT id FROM roles WHERE name = ?", (role_name,))).fetchone()
await conn.execute(
"INSERT INTO group_hostgroup_roles (user_group_id, host_group_id, role_id) VALUES (?, ?, ?)",
(group_id, host_group_id, role_row[0]),
)
await conn.commit()
return group_id
async def _grant_group_credential(conn, *, user_group_id: int, kind: str, credential_id: int) -> None:
"""Achse B (Teil D Schritt 3+5): ohne diese Freigabe scheitert seit dem
S4-Fix in Schritt 5 jede Nicht-Admin-Zuweisung eines Zugangsdatensatzes
an einen Host mit 403, selbst wenn die Achse-A-Rolle
(credentials_manage) vorhanden ist -- siehe app/admin/routes.py,
assign_rdp_credential_to_host()/map_ssh_key_to_host()."""
grant_table = {
"ssh_key": "group_ssh_key_grants",
"rdp_credential": "group_rdp_credential_grants",
"ssh_password_credential": "group_ssh_password_credential_grants",
}[kind]
col = {
"ssh_key": "ssh_key_id",
"rdp_credential": "rdp_credential_id",
"ssh_password_credential": "ssh_password_credential_id",
}[kind]
await conn.execute(
f"INSERT INTO {grant_table} (user_group_id, {col}) VALUES (?, ?)",
(user_group_id, credential_id),
)
await conn.commit()
# ---------------------------------------------------------------------------
# 1) Host-Key ermitteln: kein 500 mehr bei Verbindungsfehlern
# ---------------------------------------------------------------------------
@ -124,18 +170,14 @@ async def test_discover_host_key_success_after_auth_failure_past_kex(client, mon
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_sessions_list_requires_global_admin_not_tenant_admin(client):
async def test_sessions_list_requires_admin(client):
from app.db import get_db
conn = get_db()
superadmin_id = await _create_user(conn, "sv_super", "Correct-Horse-Battery-Staple-S1", is_admin=True)
tenant_admin_id = await _create_user(conn, "sv_tenant", "Correct-Horse-Battery-Staple-S2")
await conn.execute(
"INSERT INTO tenant_admins (user_id, tenant_id) VALUES (?, 1)", (tenant_admin_id,)
)
await conn.commit()
normal_user_id = await _create_user(conn, "sv_normal", "Correct-Horse-Battery-Staple-S2")
await _login_full(client, "sv_tenant", "Correct-Horse-Battery-Staple-S2")
await _login_full(client, "sv_normal", "Correct-Horse-Battery-Staple-S2")
resp = await client.get("/admin/sessions")
assert resp.status_code == 403, resp.text
@ -205,22 +247,29 @@ async def test_credentials_manage_role_grants_non_admin_write_access(client):
hg_id, host_id = await _setup_hostgroup_and_host(conn, group_name="cr-group", hostname="cr-host")
await _login_full(client, "cr_admin", "Correct-Horse-Battery-Staple-C1")
resp = await client.post(
"/admin/roles/grant",
json={"user_id": holder_id, "host_group_id": hg_id, "role_names": ["credentials_manage"]},
holder_group_id = await _grant_group_role(
conn, user_id=holder_id, host_group_id=hg_id, role_names=["credentials_manage"],
group_name="cr-holder-team",
)
assert resp.status_code == 200, resp.text
# Migration 0012: das Zugangsdaten-OBJEKT selbst legt weiterhin nur ein
# Admin an (analog SSH-Keys, require_admin_or_scope("rdp_credentials",
# "write")) -- der Rolleninhaber darf es aber einem Host ZUWEISEN.
resp = await client.post(
"/admin/rdp-credentials",
json={"label": "cr-test-cred", "username": "Administrator", "password": "s3hr-geheim!!", "tenant_id": 1},
json={"label": "cr-test-cred", "username": "Administrator", "password": "s3hr-geheim!!"},
)
assert resp.status_code == 201, resp.text
credential_id = resp.json()["id"]
# Teil D Schritt 5 (S4): die Zuweisung an einen Host prueft bei einem
# Nicht-Admin zusaetzlich, ob eine seiner Gruppen den Datensatz ueber
# Achse B freigegeben bekommen hat -- ohne diese Zeile wuerde der
# POST unten mit 403 scheitern, obwohl credentials_manage vorhanden ist.
await _grant_group_credential(
conn, user_group_id=holder_group_id, kind="rdp_credential", credential_id=credential_id
)
# Rolleninhaber (kein Admin!) darf Zugangsdaten lesen UND zuweisen.
client.cookies.clear()
await _login_full(client, "cr_holder", "Correct-Horse-Battery-Staple-C2")
@ -256,11 +305,10 @@ async def test_credentials_view_role_is_read_only(client):
hg_id, host_id = await _setup_hostgroup_and_host(conn, group_name="cv-group", hostname="cv-host")
await _login_full(client, "cv_admin", "Correct-Horse-Battery-Staple-C4")
resp = await client.post(
"/admin/roles/grant",
json={"user_id": viewer_id, "host_group_id": hg_id, "role_names": ["credentials_view"]},
await _grant_group_role(
conn, user_id=viewer_id, host_group_id=hg_id, role_names=["credentials_view"],
group_name="cv-viewer-team",
)
assert resp.status_code == 200, resp.text
client.cookies.clear()
await _login_full(client, "cv_viewer", "Correct-Horse-Battery-Staple-C5")
@ -285,9 +333,9 @@ async def test_catalog_hosts_reports_can_view_credentials_flag(client):
hg_id, host_id = await _setup_hostgroup_and_host(conn, group_name="cc-group", hostname="cc-host")
await _login_full(client, "cc_admin", "Correct-Horse-Battery-Staple-C6")
await client.post(
"/admin/roles/grant",
json={"user_id": user_id, "host_group_id": hg_id, "role_names": ["ssh_connect", "credentials_view"]},
await _grant_group_role(
conn, user_id=user_id, host_group_id=hg_id, role_names=["ssh_connect", "credentials_view"],
group_name="cc-user-team",
)
client.cookies.clear()