towerops/lib/towerops_web/live/helpers/access_control.ex
2026-06-13 14:52:18 -05:00

75 lines
2.5 KiB
Elixir

defmodule ToweropsWeb.Live.Helpers.AccessControl do
@moduledoc """
Centralized organization-based access control for LiveViews.
Provides functions to verify that users have access to resources
(devices, sites, alerts) within their organization scope.
"""
alias Towerops.Alerts.Alert
alias Towerops.Devices
alias Towerops.Devices.Device
alias Towerops.Repo
alias Towerops.Sites
alias Towerops.Sites.Site
@doc """
Verifies that a device belongs to the specified organization.
Returns `{:ok, device}` if access is granted, or an error tuple.
"""
@spec verify_device_access(binary(), binary()) ::
{:ok, Device.t()} | {:error, :not_found | :unauthorized}
def verify_device_access(device_id, organization_id) do
case Devices.get_device(device_id) do
%Device{organization_id: ^organization_id} = device -> {:ok, device}
nil -> {:error, :not_found}
%Device{} -> {:error, :unauthorized}
end
end
@doc """
Verifies that a site belongs to the specified organization.
Returns `{:ok, site}` if access is granted, or an error tuple.
"""
@spec verify_site_access(binary(), binary()) ::
{:ok, Site.t()} | {:error, :not_found | :unauthorized}
def verify_site_access(site_id, organization_id) do
case Sites.get_site(site_id) do
%Site{organization_id: ^organization_id} = site -> {:ok, site}
nil -> {:error, :not_found}
%Site{} -> {:error, :unauthorized}
end
end
@doc """
Verifies that an alert's device belongs to the specified organization.
Returns `{:ok, alert}` if access is granted, or an error tuple.
The alert is preloaded with `device: [site: :organization]` for access checking.
## Examples
iex> verify_alert_access(alert_id, org_id)
{:ok, %Alert{device: %Device{site: %Site{organization: %Organization{}}}}}
iex> verify_alert_access(alert_id, wrong_org_id)
{:error, :unauthorized}
iex> verify_alert_access("nonexistent", org_id)
{:error, :not_found}
"""
@spec verify_alert_access(binary(), binary()) ::
{:ok, Alert.t()} | {:error, :not_found | :unauthorized}
def verify_alert_access(alert_id, organization_id) do
with %Alert{} = alert <- Repo.get(Alert, alert_id),
alert = Repo.preload(alert, [:acknowledged_by, device: [site: :organization]]),
%Alert{device: %{site: %{organization_id: ^organization_id}}} <- alert do
{:ok, alert}
else
nil -> {:error, :not_found}
%Alert{} -> {:error, :unauthorized}
end
end
end