lib/abacPDP.ex

defmodule ABAC.PDP do
  require ABAC
  require KVS

  # PDP
  def decision(req0) do
    {req, policy} = ABAC.PIP.request(req0)
    :lists.any(&policy(req, &1), :lists.flatten(:lists.map(&:kvs.index_match(&1, :object, KVS.kvs(mod: :kvs_mnesia)), policy)))
  end

  def deny(req0) do
    {req, policy} = ABAC.PIP.request(req0)
    :lists.any(&!policy(req, &1), :lists.flatten(:lists.map(&:kvs.index_match(&1, :object, KVS.kvs(mod: :kvs_mnesia)), policy)))
  end

  def get(req0) do
    {ABAC.request() = req, policy} = ABAC.PIP.request(req0)
    :lists.flatten(:lists.map(fn x ->
      :lists.filtermap(fn ABAC.rule(values: []) -> false; ABAC.rule(values: v, object: o) -> {true, :kvs.field(o, v)} end, policy(req, x))
    end, :lists.flatten(:lists.map(&:kvs.index_match(&1, :object, KVS.kvs(mod: :kvs_mnesia)), policy))))
  end

  def parse(r), do: ABAC.PIP.parse(r)

  def policy(ABAC.request(type: t) = req, ABAC.policy(rules: r, combining: c)) when t in [:decision, :deny], do:
    :erlang.apply(:lists, c, [&rule(req, :erlang.element(2, :kvs.get(:rule, ABAC.rule_ref(&1, :id), KVS.kvs(mod: :kvs_mnesia)))), r])
  def policy(ABAC.request(type: :get) = req, ABAC.policy(rules: r, combining: :all)), do:
    :erlang.element(2, :lists.foldr(fn _, {false, _} = x -> x
                                       ABAC.rule_ref(id: i), {true, acc} ->
      rule = :erlang.element(2, :kvs.get(:rule, i, KVS.kvs(mod: :kvs_mnesia)))
      if rule(req, rule), do: {true, [rule | acc]}, else: {false, []}
    end, {true, []}, r))
  def policy(ABAC.request(type: :get) = req, ABAC.policy(rules: r)), do:
    :lists.foldr(fn ABAC.rule_ref(id: i), acc ->
      x = :erlang.element(2, :kvs.get(:rule, i, KVS.kvs(mod: :kvs_mnesia)))
      case rule(req, x) do true -> [x | acc]; _ -> acc end
    end, [], r)
  def policy(ABAC.request(type: :decision), _), do: false
  def policy(ABAC.request(type: :deny), _), do: true
  def policy(ABAC.request(type: :get), _), do: []

  def rule(
    ABAC.request(endpoint: e, resources: resources) = req,
    ABAC.rule(api_endpoint: e, type: t, object_condition: [], object: o, subject_condition: c, resource_match: rm) = r
  ), do:
    (x = :lists.filter(fn ^o -> true; _ -> false end, resources);
     result(t, x != [] and :erlang.apply(:lists, rm, [fn _ -> :erlang.apply(:"Elixir.ABAC.Condition", c, [req, r]) end, x])))
  def rule(
    ABAC.request(endpoint: e, resources: resources) = req,
    ABAC.rule(api_endpoint: e, type: t, object_condition: oc, object: o, subject_condition: c, resource_match: rm) = r
  ), do:
    (x = :lists.filter(&(:erlang.is_record(&1, :erlang.element(1, o))), resources);
     result(t, x != [] and :erlang.apply(:lists, rm, [fn x -> :erlang.apply(:"Elixir.ABAC.Condition", c, [req, r]) and :erlang.apply(:"Elixir.ABAC.Condition", oc, [x, r]) end, x])))
  def rule(_, _), do: false

  defp result(:permit, x), do: x
  defp result(:deny, x), do: !x

end