defmodule Carny.Policy do @moduledoc """ Policies are lists of statements that you can validate an identifier against. """ alias __MODULE__ alias Carny.{Statement, Identifier} defstruct statements: [] @doc ~S""" Adds a statement to a given policy ## Examples iex> Carny.Policy.add_statement(%Carny.Policy{statements: [%Carny.Statement{effect: :deny}]}, %Carny.Statement{}) %Carny.Policy{statements: [%Carny.Statement{}, %Carny.Statement{effect: :deny}]} """ def add_statement(%Policy{} = policy, %Statement{} = statement) do Map.update!(policy, :statements, fn statements -> [statement | statements] end) end @doc """ Does the id pass a specific policy? The use case here is we have already looked up a policy for our given resource and want to make sure that it has access to some other resource (id). ## Examples iex> Carny.Policy.authorized?(%Carny.Identifier{}, %Carny.Policy{statements: [%Carny.Statement{resources: [%Carny.Identifier{}]}]}) true """ def authorized?(%Identifier{} = id, %Policy{} = policy) do # As soon as we reach a "true" value then halt # because we know it should have access Enum.reduce_while(policy.statements, false, fn statement, false -> {:cont, Statement.passes?(id, statement)} _statement, true -> {:halt, true} end) end end