balls/lib/balls_pds/ecto/schema/object_read_agent.ex

129 lines
3.4 KiB
Elixir

defmodule BallsPDS.Ecto.Schema.ObjectReadAgent do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
import BallsPDS.Util.ACL, only: [is_valid_acl?: 1]
alias BallsPDS.Ecto.Schema.Object
alias BallsPDS.Ecto.Schema.Agent
alias BallsPDS.Repo
@primary_key false
schema "object_read_agents" do
belongs_to(:object, BallsPDS.Ecto.Schema.Object, foreign_key: :object_id)
belongs_to(:agent, BallsPDS.Ecto.Schema.Agent, foreign_key: :agent_id)
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [
:object_id,
:agent_id
])
|> validate_required([
:object_id,
:agent_id
])
|> foreign_key_constraint(:object_id)
|> foreign_key_constraint(:agent_id)
end
def delete(object_id, agent_id) when is_integer(object_id) and is_integer(agent_id) do
query =
from(ora in __MODULE__,
where: ora.object_id == ^object_id and ora.agent_id == ^agent_id
)
Repo.delete_all(query)
end
def delete(object_id) when is_binary(object_id) do
query =
from(ora in __MODULE__,
where: ora.object_id == ^object_id
)
Repo.delete_all(query)
end
def insert(object_id, agent_id) when is_integer(object_id) and is_integer(agent_id) do
Repo.insert(
%__MODULE__{object_id: object_id, agent_id: agent_id},
on_conflict: :nothing
)
end
def revoke_read(%Object{id: object_id}, acl) when is_binary(acl) do
with {:exists, %Agent{id: agent_id}} <- {:exists, Agent.get_by_acl(acl)},
{:delete, {:ok, _}} <- {:delete, delete(object_id, agent_id)} do
else
{:exists, nil} ->
{:error, :no_agent}
{:delete, {:error, error}} ->
{:error, {:delete, error}}
end
end
# still leaves public.
def revoke_all_read(%Object{id: object_id}) do
delete(object_id)
end
def authorize_read(%Object{id: object_id}, acl) when is_binary(acl) do
with {:valid_acl, true} <- {:valid_acl, is_valid_acl?(acl)},
{:agent, {:ok, %Agent{id: agent_id, disabled: false}}} <-
{:agent, Agent.get_or_create_by_acl(acl)},
{:insert, {:ok, _}} <- {:insert, insert(object_id, agent_id)} do
:ok
else
{:valid_acl, false} -> {:error, :invalid_acl}
{:agent, {:error, _} = error} -> error
{:agent, {:ok, %{disabled: true}}} -> {:error, :disabled_agent}
{:insert, {:error, _} = error} -> error
end
end
def authorize_read(%Object{} = object, acls = []) do
errors =
Enum.reduce(acls, [], fn
acl, errors when is_binary(acl) ->
case authorize_read(object, acl) do
:ok -> errors
{:error, error} -> [{acl, error} | errors]
end
acl, errors ->
[{acl, :invalid_acl} | errors]
end)
if length(errors == 0) do
:ok
else
{:error, errors}
end
end
def is_authorized_read?(%Object{id: object_id}, acl) when is_binary(acl) do
query =
from(oa in __MODULE__,
join: a in Agent,
on: oa.agent_id == a.id,
where: a.acl == ^acl and oa.object_id == ^object_id and a.disabled == false
)
Repo.exists?(query)
end
def get_read_agents(%Object{id: object_id}) do
query =
from(oa in __MODULE__,
join: a in Agent,
on: oa.agent_id == a.id,
where: oa.object_id == ^object_id and a.disabled == false,
select: a.acl
)
Repo.all(query)
end
end