towerops/lib/towerops_web/scoped_resource.ex
Graham McIntire 095c5d3236 fix: M1 — prevent cross-org resource existence probing via scoped fetch
Use Repo.get_by(schema, id: id, organization_id: organization_id) instead of
Repo.get + pattern match so that resources from wrong orgs return :not_found
instead of :forbidden, preventing org membership discovery.
2026-05-12 13:44:19 -05:00

24 lines
815 B
Elixir

defmodule ToweropsWeb.ScopedResource do
@moduledoc """
Shared helpers for loading organization-scoped resources.
"""
alias Towerops.Repo
@spec fetch(module(), Ecto.UUID.t(), Ecto.UUID.t()) ::
{:ok, struct()} | {:error, :not_found | :forbidden}
def fetch(schema, id, organization_id) do
case Repo.get_by(schema, id: id, organization_id: organization_id) do
nil -> {:error, :not_found}
resource -> {:ok, resource}
end
end
@spec fetch_preload(module(), Ecto.UUID.t(), Ecto.UUID.t(), atom() | list() | keyword()) ::
{:ok, struct()} | {:error, :not_found | :forbidden}
def fetch_preload(schema, id, organization_id, preloads) do
with {:ok, resource} <- fetch(schema, id, organization_id) do
{:ok, Repo.preload(resource, preloads)}
end
end
end