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:
@@ -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))
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user