defmodule Carny.Statement do @moduledoc """ Associates a bucket of resources with an "allow" or "deny". On their own not so useful main use is to be composed into Policies. """ alias __MODULE__ alias Carny.Identifier defstruct effect: "allow", resources: [], privileges: [] @doc """ Checks if an identifier passes a statement. ## Examples iex> Carny.Statement.passes?("start", %Carny.Identifier{}, %Carny.Statement{privileges: ["start"], resources: [%Carny.Identifier{}]}) true """ def passes?( privilege, %Identifier{} = identifier, %Statement{resources: resources, privileges: privileges, effect: "allow"} ) when length(resources) > 0 do case privilege in privileges do true -> Enum.reduce_while(resources, true, fn resource, true -> {:cont, Identifier.validate_against(identifier, resource)} _resource, false -> {:halt, false} end) false -> false end end def passes?( %Identifier{} = identifier, %Statement{resources: resources, effect: "deny"} = statement ) when length(resources) > 0 do not passes?(identifier, statement) end def passes?(_, _), do: false def from_map(%{"resources" => resources} = statement) do resources = Enum.map(resources, fn id -> id |> atom_map() |> Identifier.from_map() end) struct(Statement, atom_map(%{statement | "resources" => resources})) end def from_map(statement), do: struct(Statement, statement) defp atom_map(t), do: Map.new(t, fn {k, v} -> {String.to_atom(k), v} end) end