fix: redis cache invalidation in permission service + permission check in entity_permissions route + upsert cache invalidation + remove TopBar quick-create

This commit is contained in:
Agent Zero
2026-07-29 10:45:45 +02:00
parent 0cebd23e3b
commit 81ae5b7cb6
2 changed files with 32 additions and 8 deletions
+12 -1
View File
@@ -49,10 +49,19 @@ async def create_entity_permission(
body: EntityPermissionCreate, body: EntityPermissionCreate,
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user), 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.""" """Grant or update a permission on any entity for a user, group, role, or guest."""
tenant_id = uuid.UUID(current_user["tenant_id"]) tenant_id = uuid.UUID(current_user["tenant_id"])
user_id = uuid.UUID(current_user["user_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 # Rate limit: max 50 permission changes per minute per user
await check_rate_limit( await check_rate_limit(
f"perm_change:{user_id}", f"perm_change:{user_id}",
@@ -60,7 +69,7 @@ async def create_entity_permission(
_PERM_RATE_LIMIT_WINDOW, _PERM_RATE_LIMIT_WINDOW,
) )
try: try:
return await entity_permission_service.create_permission( result = await entity_permission_service.create_permission(
db, db,
tenant_id, tenant_id,
entity_type, entity_type,
@@ -70,7 +79,9 @@ async def create_entity_permission(
body.permission_level, body.permission_level,
body.expires_at, body.expires_at,
created_by=user_id, created_by=user_id,
redis=redis,
) )
return result
except ValueError as e: except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) raise HTTPException(status_code=400, detail=str(e))
+20 -7
View File
@@ -109,6 +109,7 @@ async def create_permission(
permission_level: str, permission_level: str,
expires_at: datetime | None = None, expires_at: datetime | None = None,
created_by: uuid.UUID | None = None, created_by: uuid.UUID | None = None,
redis: Any = None,
) -> dict: ) -> dict:
"""Create or update a permission entry (upsert).""" """Create or update a permission entry (upsert)."""
entity_uuid = uuid.UUID(entity_id) entity_uuid = uuid.UUID(entity_id)
@@ -149,6 +150,17 @@ async def create_permission(
entity_type=entity_type, entity_type=entity_type,
entity_id=entity_uuid, 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)) return _serialize_permission(existing, names.get(existing.principal_id))
perm = EntityPermission( perm = EntityPermission(
@@ -167,7 +179,7 @@ async def create_permission(
# Invalidate cache for this principal # Invalidate cache for this principal
if principal_type == "user": 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": elif principal_type == "group":
# Invalidate for all group members # Invalidate for all group members
members_q = await db.execute( members_q = await db.execute(
@@ -176,7 +188,7 @@ async def create_permission(
.where(UserGroup.tenant_id == tenant_id) .where(UserGroup.tenant_id == tenant_id)
) )
for (uid,) in members_q: 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 # Audit log for new permission
await log_audit( await log_audit(
@@ -207,6 +219,7 @@ async def update_permission(
permission_id: str, permission_id: str,
permission_level: str, permission_level: str,
expires_at: datetime | None = None, expires_at: datetime | None = None,
redis: Any = None,
) -> dict: ) -> dict:
"""Update an existing permission entry.""" """Update an existing permission entry."""
perm_uuid = uuid.UUID(permission_id) perm_uuid = uuid.UUID(permission_id)
@@ -232,7 +245,7 @@ async def update_permission(
# Invalidate cache # Invalidate cache
if old_principal_type == "user": 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": elif old_principal_type == "group":
members_q = await db.execute( members_q = await db.execute(
select(UserGroup.user_id) select(UserGroup.user_id)
@@ -240,7 +253,7 @@ async def update_permission(
.where(UserGroup.tenant_id == tenant_id) .where(UserGroup.tenant_id == tenant_id)
) )
for (uid,) in members_q: 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 # Audit log for permission update
await log_audit( await log_audit(
@@ -256,7 +269,7 @@ async def update_permission(
async def delete_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: ) -> None:
"""Delete a permission entry.""" """Delete a permission entry."""
perm_uuid = uuid.UUID(permission_id) perm_uuid = uuid.UUID(permission_id)
@@ -298,7 +311,7 @@ async def delete_permission(
# Invalidate cache # Invalidate cache
if old_principal_type == "user": 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": elif old_principal_type == "group":
members_q = await db.execute( members_q = await db.execute(
select(UserGroup.user_id) select(UserGroup.user_id)
@@ -306,7 +319,7 @@ async def delete_permission(
.where(UserGroup.tenant_id == tenant_id) .where(UserGroup.tenant_id == tenant_id)
) )
for (uid,) in members_q: 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( async def get_effective_access(