fix: outbox consumer_inbox idempotency logic + tenant_plugin_activation per-tenant check

This commit is contained in:
Agent Zero
2026-07-29 13:02:33 +02:00
parent 0f4e51c4b3
commit 648d8d89d6
4 changed files with 89 additions and 8 deletions
+28 -7
View File
@@ -290,13 +290,10 @@ async def get_current_user_id(
def require_active_plugin(plugin_name: str):
"""FastAPI dependency factory: require that a plugin is active.
Returns 403 if the plugin is not active in the permission registry.
This allows routes to be registered at app creation time while
enforcing activation status at request time.
Checks both global activation (permission registry) and per-tenant
activation (tenant_plugin_activation table).
Note: Does NOT depend on get_current_user — individual route handlers
already have their own auth dependencies (require_permission, etc.).
This check only verifies plugin activation status.
Returns 403 if the plugin is not active.
"""
async def _check() -> None:
from app.core.permission_registry import get_permission_registry
@@ -310,11 +307,35 @@ def require_active_plugin(plugin_name: str):
"code": "plugin_inactive",
},
)
# Per-tenant activation check (P1.4 fix)
# If there's an entry in tenant_plugin_activation for this
# tenant+plugin with is_active=false, deny access.
# If no entry exists, default to active (backward compatible).
from app.core.db import async_session_maker
from sqlalchemy import text
async with async_session_maker() as db:
# Get tenant_id from current session context (RLS)
result = await db.execute(
text("""
SELECT is_active FROM tenant_plugin_activation
WHERE plugin_name = :name
AND tenant_id = current_setting('app.current_tenant_id', true)::uuid
"""),
{"name": plugin_name},
)
row = result.first()
if row is not None and not row[0]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail={
"detail": f"Plugin '{plugin_name}' is not active for this tenant",
"code": "plugin_inactive_tenant",
},
)
except HTTPException:
raise
except Exception as exc:
# Fail-closed: if registry check fails, deny access (P1.2 fix)
# Previously this was fail-open (pass) which allowed access on errors
logger.error("Plugin activation check failed for '%s': %s", plugin_name, exc)
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,