"""RBAC-Durchsetzung: Rolle × Hostgruppe (siehe Konzept 4.6). Eine Rolle gilt fuer einen User entweder, wenn sie ihm DIREKT vergeben wurde (user_hostgroup_roles), ODER wenn sie einer Benutzergruppe (user_groups) vergeben wurde, in der der User Mitglied ist (group_hostgroup_roles) -- volle Rollen-Vererbung: jedes Gruppenmitglied erhaelt automatisch alle der Gruppe gewaehrten Rollen, ohne individuellen Eintrag.""" from __future__ import annotations import aiosqlite async def user_has_role( conn: aiosqlite.Connection, *, user_id: int, host_group_id: int, role_name: str ) -> bool: cursor = await conn.execute( """ SELECT 1 FROM user_hostgroup_roles uhr JOIN roles r ON r.id = uhr.role_id WHERE uhr.user_id = ? AND uhr.host_group_id = ? AND r.name = ? AND (uhr.expires_at IS NULL OR uhr.expires_at > strftime('%Y-%m-%dT%H:%M:%fZ','now')) UNION SELECT 1 FROM group_hostgroup_roles ghr JOIN roles r ON r.id = ghr.role_id JOIN user_group_members ugm ON ugm.user_group_id = ghr.user_group_id WHERE ugm.user_id = ? AND ghr.host_group_id = ? AND r.name = ? AND (ghr.expires_at IS NULL OR ghr.expires_at > strftime('%Y-%m-%dT%H:%M:%fZ','now')) LIMIT 1 """, (user_id, host_group_id, role_name, user_id, host_group_id, role_name), ) row = await cursor.fetchone() return row is not None async def user_has_role_for_host( conn: aiosqlite.Connection, *, user_id: int, host_id: int, role_name: str ) -> bool: cursor = await conn.execute("SELECT host_group_id FROM hosts WHERE id = ?", (host_id,)) row = await cursor.fetchone() if row is None: return False return await user_has_role(conn, user_id=user_id, host_group_id=row[0], role_name=role_name)