diff --git a/app/routes/entity_permissions.py b/app/routes/entity_permissions.py index 413f9bb..dd5178f 100644 --- a/app/routes/entity_permissions.py +++ b/app/routes/entity_permissions.py @@ -49,10 +49,19 @@ async def create_entity_permission( body: EntityPermissionCreate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), + redis = Depends(get_redis_dep), ): """Grant or update a permission on any entity for a user, group, role, or guest.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) + is_system_admin = current_user.get("is_system_admin", False) + # Permission check: only owner/admin/system_admin can manage permissions + if not is_system_admin: + access = await entity_permission_service.get_effective_access( + db, tenant_id, user_id, entity_type, uuid.UUID(entity_id) + ) + if access not in ("owner", "admin", "delete"): + raise HTTPException(status_code=403, detail="Sie benötigen Admin-Rechte auf diesen Datensatz, um Berechtigungen zu verwalten") # Rate limit: max 50 permission changes per minute per user await check_rate_limit( f"perm_change:{user_id}", @@ -60,7 +69,7 @@ async def create_entity_permission( _PERM_RATE_LIMIT_WINDOW, ) try: - return await entity_permission_service.create_permission( + result = await entity_permission_service.create_permission( db, tenant_id, entity_type, @@ -70,7 +79,9 @@ async def create_entity_permission( body.permission_level, body.expires_at, created_by=user_id, + redis=redis, ) + return result except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) diff --git a/app/services/entity_permission_service.py b/app/services/entity_permission_service.py index 1ef919b..b83374e 100644 --- a/app/services/entity_permission_service.py +++ b/app/services/entity_permission_service.py @@ -109,6 +109,7 @@ async def create_permission( permission_level: str, expires_at: datetime | None = None, created_by: uuid.UUID | None = None, + redis: Any = None, ) -> dict: """Create or update a permission entry (upsert).""" entity_uuid = uuid.UUID(entity_id) @@ -149,6 +150,17 @@ async def create_permission( entity_type=entity_type, entity_id=entity_uuid, ) + # Invalidate cache for upsert + if principal_type == "user": + await _invalidate_user_cache(redis, tenant_id, principal_uuid, entity_type) + elif principal_type == "group": + members_q2 = await db.execute( + select(UserGroup.user_id) + .where(UserGroup.group_id == principal_uuid) + .where(UserGroup.tenant_id == tenant_id) + ) + for (uid2,) in members_q2: + await _invalidate_user_cache(redis, tenant_id, uid2, entity_type) return _serialize_permission(existing, names.get(existing.principal_id)) perm = EntityPermission( @@ -167,7 +179,7 @@ async def create_permission( # Invalidate cache for this principal if principal_type == "user": - await _invalidate_user_cache(None, tenant_id, principal_uuid, entity_type) + await _invalidate_user_cache(redis, tenant_id, principal_uuid, entity_type) elif principal_type == "group": # Invalidate for all group members members_q = await db.execute( @@ -176,7 +188,7 @@ async def create_permission( .where(UserGroup.tenant_id == tenant_id) ) for (uid,) in members_q: - await _invalidate_user_cache(None, tenant_id, uid, entity_type) + await _invalidate_user_cache(redis, tenant_id, uid, entity_type) # Audit log for new permission await log_audit( @@ -207,6 +219,7 @@ async def update_permission( permission_id: str, permission_level: str, expires_at: datetime | None = None, + redis: Any = None, ) -> dict: """Update an existing permission entry.""" perm_uuid = uuid.UUID(permission_id) @@ -232,7 +245,7 @@ async def update_permission( # Invalidate cache if old_principal_type == "user": - await _invalidate_user_cache(None, tenant_id, old_principal_id, old_entity_type) + await _invalidate_user_cache(redis, tenant_id, old_principal_id, old_entity_type) elif old_principal_type == "group": members_q = await db.execute( select(UserGroup.user_id) @@ -240,7 +253,7 @@ async def update_permission( .where(UserGroup.tenant_id == tenant_id) ) for (uid,) in members_q: - await _invalidate_user_cache(None, tenant_id, uid, old_entity_type) + await _invalidate_user_cache(redis, tenant_id, uid, old_entity_type) # Audit log for permission update await log_audit( @@ -256,7 +269,7 @@ async def update_permission( async def delete_permission( - db: AsyncSession, tenant_id: uuid.UUID, permission_id: str + db: AsyncSession, tenant_id: uuid.UUID, permission_id: str, redis: Any = None ) -> None: """Delete a permission entry.""" perm_uuid = uuid.UUID(permission_id) @@ -298,7 +311,7 @@ async def delete_permission( # Invalidate cache if old_principal_type == "user": - await _invalidate_user_cache(None, tenant_id, old_principal_id, old_entity_type) + await _invalidate_user_cache(redis, tenant_id, old_principal_id, old_entity_type) elif old_principal_type == "group": members_q = await db.execute( select(UserGroup.user_id) @@ -306,7 +319,7 @@ async def delete_permission( .where(UserGroup.tenant_id == tenant_id) ) for (uid,) in members_q: - await _invalidate_user_cache(None, tenant_id, uid, old_entity_type) + await _invalidate_user_cache(redis, tenant_id, uid, old_entity_type) async def get_effective_access(