feat(#357): custom_field_definitions generisch — W4b-Muster (422/403-Entity-Checks, {items,total}-Shape, ACL-Fix), zentrale Helper, Plural-Ableitungs-Fix
This commit is contained in:
@@ -84,10 +84,15 @@ def get_entity_read_permission(entity_type: str) -> str:
|
||||
owner = ENTITY_PLUGIN_OWNERS.get(entity_type)
|
||||
if owner:
|
||||
return f"{owner}:read"
|
||||
# Core entities: derive from module name (e.g. workflows → workflows:read)
|
||||
module = entity_type.rstrip("s")
|
||||
candidates = [k for k in _core_module_keys(module, "read")]
|
||||
return candidates[0] if candidates else "contacts:read"
|
||||
# Core entities: derive from the module name in CORE_PERMISSIONS. Modules
|
||||
# are mostly plural ("workflows", "addresses") while entity types are
|
||||
# mostly singular ("workflow", "address") — try exact, singular and
|
||||
# plural forms before the contacts:read fallback.
|
||||
for module in (entity_type, entity_type.rstrip("s"), f"{entity_type}s", f"{entity_type}es"):
|
||||
candidates = _core_module_keys(module, "read")
|
||||
if candidates:
|
||||
return candidates[0]
|
||||
return "contacts:read"
|
||||
|
||||
|
||||
def _core_module_keys(module: str, action: str) -> list[str]:
|
||||
@@ -100,6 +105,41 @@ def _core_module_keys(module: str, action: str) -> list[str]:
|
||||
if p.get("module") == module and p["key"].endswith(f":{action}")
|
||||
]
|
||||
|
||||
|
||||
def validate_entity_type(entity_type: str) -> None:
|
||||
"""Validate entity_type against ENTITY_MODELS (W4b pattern).
|
||||
|
||||
Central helper for entity-typed CRUD (saved filters/views,
|
||||
custom field definitions). Raises fastapi HTTPException 422
|
||||
with the valid types so clients can self-correct.
|
||||
"""
|
||||
if entity_type not in ENTITY_MODELS:
|
||||
from fastapi import HTTPException
|
||||
|
||||
raise HTTPException(422, detail={
|
||||
"detail": f"Invalid entity_type: {entity_type}",
|
||||
"code": "invalid_entity_type",
|
||||
"valid_types": sorted(ENTITY_MODELS.keys()),
|
||||
})
|
||||
|
||||
|
||||
def check_entity_read_permission(current_user: dict, entity_type: str) -> None:
|
||||
"""Check that the user may read the entity type's owning module (W4b).
|
||||
|
||||
Raises fastapi HTTPException 403 when the derived module read
|
||||
permission (e.g. contacts:read, workflows:read) is missing.
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.core.permissions import check_permission
|
||||
|
||||
perm = get_entity_read_permission(entity_type)
|
||||
if not check_permission(current_user, perm):
|
||||
raise HTTPException(403, detail={
|
||||
"detail": f"Permission '{perm}' required",
|
||||
"code": "forbidden",
|
||||
})
|
||||
|
||||
# Core models with OwnedMixin (Phase 2 additions)
|
||||
try:
|
||||
from app.models.entity_attachment import EntityAttachment
|
||||
@@ -130,6 +170,7 @@ def register_entity_model(
|
||||
def unregister_entity_model(entity_type: str) -> None:
|
||||
"""Unregister an entity model (called during plugin deactivation)."""
|
||||
ENTITY_MODELS.pop(entity_type, None)
|
||||
ENTITY_PLUGIN_OWNERS.pop(entity_type, None)
|
||||
|
||||
|
||||
def _get_entity_model(entity_type: str) -> type:
|
||||
|
||||
Reference in New Issue
Block a user