129 lines
3.4 KiB
Elixir
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
|