arcadia-app issues the role slug "platform-admin" (hyphen) — confirmed from a live arcadia-dev JWT (roles: ["admin","platform-admin"]). Every authorization check here tested for "platform_admin" (underscore), so real operator tokens got 403 on billing / dashboard / drift and an empty tenant-scoped result on inventory. The smoke tests missed it because Guardian.mint_dev_token hardcoded the underscore form — fixed there too, so the dev helper now matches what arcadia-app actually emits. Replaced the string literal "platform_admin" -> "platform-admin" in all six controllers + guardian.ex. The platform_admin?/1 function names keep underscores (Elixir identifiers can't contain hyphens) — only the role string changed. Verified: with a platform-admin token, /inventory, /billing/balance, /dashboard/margin and /drift all return 200. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
1.9 KiB
Elixir
71 lines
1.9 KiB
Elixir
defmodule ArcadiaCloudWeb.InvoiceController do
|
|
@moduledoc """
|
|
Tenant invoices (revenue side). platform_admin sees all tenants;
|
|
others are scoped to their own tenant_id.
|
|
"""
|
|
|
|
use ArcadiaCloudWeb, :controller
|
|
|
|
alias ArcadiaCloud.Invoicing
|
|
|
|
def index(conn, params) do
|
|
identity = conn.assigns.current_identity
|
|
|
|
opts =
|
|
if platform_admin?(identity),
|
|
do: [tenant_id: params["tenant_id"], status: params["status"]],
|
|
else: [tenant_id: identity.tenant_id, status: params["status"]]
|
|
|
|
invoices = Invoicing.list_invoices(opts) |> Enum.map(&shape/1)
|
|
json(conn, %{invoices: invoices, count: length(invoices)})
|
|
end
|
|
|
|
def show(conn, %{"id" => id}) do
|
|
identity = conn.assigns.current_identity
|
|
|
|
case Invoicing.get_invoice(id) do
|
|
nil ->
|
|
conn |> put_status(:not_found) |> json(%{error: "not_found"})
|
|
|
|
invoice ->
|
|
if platform_admin?(identity) or invoice.tenant_id == identity.tenant_id do
|
|
json(conn, %{invoice: shape(invoice), lines: Enum.map(invoice.lines, &shape_line/1)})
|
|
else
|
|
conn |> put_status(:forbidden) |> json(%{error: "forbidden"})
|
|
end
|
|
end
|
|
end
|
|
|
|
defp platform_admin?(%{roles: roles}) when is_list(roles), do: "platform-admin" in roles
|
|
defp platform_admin?(_), do: false
|
|
|
|
defp shape(i) do
|
|
%{
|
|
id: i.id,
|
|
tenant_id: i.tenant_id,
|
|
period_start: i.period_start,
|
|
period_end: i.period_end,
|
|
currency: i.currency,
|
|
subtotal_cents: i.subtotal_cents,
|
|
tax_cents: i.tax_cents,
|
|
total_cents: i.total_cents,
|
|
status: i.status,
|
|
issued_at: i.issued_at,
|
|
pushed_to_finance_at: i.pushed_to_finance_at
|
|
}
|
|
end
|
|
|
|
defp shape_line(l) do
|
|
%{
|
|
kind: l.kind,
|
|
deployment_id: l.deployment_id,
|
|
resource_kind: l.resource_kind,
|
|
description: l.description,
|
|
qty: l.qty,
|
|
unit: l.unit,
|
|
unit_price_cents: l.unit_price_cents,
|
|
amount_cents: l.amount_cents
|
|
}
|
|
end
|
|
end
|