cc80310cb520c3310f07c0cdaec5bff660b2805d
Providers/Modelos reaproveitam os endpoints /ai/providers e /ai/models já existentes (só filtram scope GLOBAL na tela); Modelos expõe custo unitário (inputCost/outputCost/audioCost) que a tela de tenant nunca mostra, porque só platform precisa cadastrar preço. GET /platform/ai-usage (novo) agrega uso bruto de AIUsageRecord de todos os tenants no mês corrente (Uso) e estima custo casando cada registro com o AIModel correspondente (Custos) — quando não dá pra casar, o registro fica de fora da soma e o tenant é marcado costIncomplete, nunca um número inventado. Achado real de autorização ao revisar AIModelsController antes de construir a tela de Modelos: POST/DELETE /ai/models não checava isPlatformUser quando o alvo era scope=GLOBAL — como a RLS híbrida (OR tenant_id IS NULL) deixa qualquer tenant ENXERGAR um provider/modelo GLOBAL, qualquer Tenant Admin com `ai.manage` (permission de escopo TENANT) conseguia injetar um modelo no catálogo global ou desabilitar um modelo GLOBAL só sabendo o id. O endpoint irmão (AIProvidersController) já tinha o check certo; corrigido com o mesmo padrão. Confirmado com um teste de ataque real: 403 depois do fix (era 201/sucesso antes), com regressão confirmando que BYOK do próprio tenant continua funcionando normalmente. Testado ponta a ponta: provider+modelo GLOBAL com custo real, uso de teste inserido direto no Postgres, /platform/ai-usage devolvendo o valor exato esperado (bate com a conta manual), tudo removido no final. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
Description
Languages
TypeScript
98.3%
Dockerfile
1.3%
CSS
0.3%
Shell
0.1%