defmodule Carny.Policy do @moduledoc """ Policies are lists of statements that you can validate an identifier against. """ alias Carny.Statement 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, 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?("start", %Carny.Identifier{}, %Carny.Policy{statements: [%Carny.Statement{privileges: ["start"], resources: [%Carny.Identifier{}]}]}) true """ def authorized?(privilege, id, 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?(privilege, id, statement)} _statement, true -> {:halt, true} end) end end