Add role-based filtering and imporve code
This commit is contained in:
@@ -209,7 +209,7 @@ async def update_compliance_record(
|
||||
|
||||
@router.delete(
|
||||
route_builder.build_resource_detail_route("food-safety/compliance", "compliance_id"),
|
||||
status_code=status.HTTP_204_NO_CONTENT
|
||||
status_code=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
@require_user_role(['admin', 'owner'])
|
||||
async def delete_compliance_record(
|
||||
@@ -218,7 +218,33 @@ async def delete_compliance_record(
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Delete (soft delete) compliance record"""
|
||||
"""
|
||||
Compliance records CANNOT be deleted for regulatory compliance.
|
||||
Use the archive endpoint to mark records as inactive.
|
||||
"""
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail={
|
||||
"error": "compliance_records_cannot_be_deleted",
|
||||
"message": "Compliance records cannot be deleted for regulatory compliance. Use PUT /food-safety/compliance/{id}/archive to archive records instead.",
|
||||
"reason": "Food safety compliance records must be retained for regulatory audits",
|
||||
"alternative_endpoint": f"/api/v1/tenants/{tenant_id}/inventory/food-safety/compliance/{compliance_id}/archive"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.put(
|
||||
route_builder.build_nested_resource_route("food-safety/compliance", "compliance_id", "archive"),
|
||||
response_model=dict
|
||||
)
|
||||
@require_user_role(['admin', 'owner'])
|
||||
async def archive_compliance_record(
|
||||
compliance_id: UUID = Path(...),
|
||||
tenant_id: UUID = Path(...),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Archive (soft delete) compliance record - marks as inactive but retains for audit"""
|
||||
try:
|
||||
query = """
|
||||
UPDATE food_safety_compliance
|
||||
@@ -228,7 +254,7 @@ async def delete_compliance_record(
|
||||
result = await db.execute(query, {
|
||||
"compliance_id": compliance_id,
|
||||
"tenant_id": tenant_id,
|
||||
"user_id": UUID(current_user["sub"])
|
||||
"user_id": UUID(current_user["user_id"])
|
||||
})
|
||||
|
||||
if result.rowcount == 0:
|
||||
@@ -238,13 +264,38 @@ async def delete_compliance_record(
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
return None
|
||||
|
||||
# Log audit event for archiving compliance record
|
||||
try:
|
||||
from shared.security import create_audit_logger, AuditSeverity, AuditAction
|
||||
audit_logger = create_audit_logger("inventory-service")
|
||||
await audit_logger.log_event(
|
||||
db_session=db,
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user["user_id"],
|
||||
action="archive",
|
||||
resource_type="compliance_record",
|
||||
resource_id=str(compliance_id),
|
||||
severity=AuditSeverity.HIGH.value,
|
||||
description=f"Archived compliance record (retained for regulatory compliance)",
|
||||
endpoint=f"/food-safety/compliance/{compliance_id}/archive",
|
||||
method="PUT"
|
||||
)
|
||||
except Exception as audit_error:
|
||||
logger.warning("Failed to log audit event", error=str(audit_error))
|
||||
|
||||
return {
|
||||
"message": "Compliance record archived successfully",
|
||||
"compliance_id": str(compliance_id),
|
||||
"archived": True,
|
||||
"note": "Record retained for regulatory compliance audits"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Error deleting compliance record", error=str(e))
|
||||
logger.error("Error archiving compliance record", error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to delete compliance record"
|
||||
detail="Failed to archive compliance record"
|
||||
)
|
||||
|
||||
@@ -22,12 +22,16 @@ from app.schemas.inventory import (
|
||||
from shared.auth.decorators import get_current_user_dep
|
||||
from shared.auth.access_control import require_user_role, admin_role_required, owner_role_required
|
||||
from shared.routing import RouteBuilder
|
||||
from shared.security import create_audit_logger, AuditSeverity, AuditAction
|
||||
|
||||
# Create route builder for consistent URL structure
|
||||
route_builder = RouteBuilder('inventory')
|
||||
|
||||
router = APIRouter(tags=["ingredients"])
|
||||
|
||||
# Initialize audit logger
|
||||
audit_logger = create_audit_logger("inventory-service")
|
||||
|
||||
# Helper function to extract user ID from user object
|
||||
def get_current_user_id(current_user: dict = Depends(get_current_user_dep)) -> UUID:
|
||||
"""Extract user ID from current user context"""
|
||||
@@ -264,6 +268,25 @@ async def hard_delete_ingredient(
|
||||
try:
|
||||
service = InventoryService()
|
||||
deletion_summary = await service.hard_delete_ingredient(ingredient_id, tenant_id)
|
||||
|
||||
# Log audit event for hard deletion
|
||||
try:
|
||||
await audit_logger.log_deletion(
|
||||
db_session=db,
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user["user_id"],
|
||||
resource_type="ingredient",
|
||||
resource_id=str(ingredient_id),
|
||||
resource_data=deletion_summary,
|
||||
description=f"Hard deleted ingredient and all associated data",
|
||||
endpoint=f"/ingredients/{ingredient_id}/hard",
|
||||
method="DELETE"
|
||||
)
|
||||
except Exception as audit_error:
|
||||
import structlog
|
||||
logger = structlog.get_logger()
|
||||
logger.warning("Failed to log audit event", error=str(audit_error))
|
||||
|
||||
return deletion_summary
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user