Packages

ash

3.4.55
3.29.3 3.29.2 3.29.1 3.29.0 3.28.0 3.27.8 3.27.7 3.27.6 3.27.5 3.27.4 3.27.3 3.27.2 3.27.1 3.27.0 3.26.0 3.25.2 3.25.1 3.25.0 retired 3.24.7 3.24.6 3.24.5 3.24.4 3.24.3 3.24.2 3.24.1 3.24.0 3.23.1 3.23.0 3.22.2 3.22.1 3.22.0 3.21.3 3.21.2 3.21.1 3.21.0 3.20.0 3.19.3 3.19.2 3.19.1 3.19.0 3.18.0 3.17.1 3.17.0 3.16.0 3.15.0 3.14.1 3.14.0 retired 3.13.2 3.13.1 3.13.0 3.12.0 3.11.3 3.11.2 3.11.1 3.11.0 3.10.1 3.10.0 3.9.0 3.8.0 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 retired 3.6.3 retired 3.6.2 3.6.1 3.6.0 3.5.43 3.5.42 3.5.41 3.5.40 3.5.39 3.5.38 3.5.37 3.5.36 3.5.35 3.5.34 3.5.33 3.5.32 3.5.31 3.5.30 3.5.29 3.5.28 3.5.27 3.5.26 3.5.25 3.5.24 3.5.23 3.5.22 3.5.21 3.5.20 3.5.19 3.5.18 3.5.17 3.5.16 3.5.15 3.5.14 3.5.13 3.5.12 3.5.11 3.5.10 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.74 retired 3.4.73 3.4.72 3.4.71 3.4.70 3.4.69 3.4.68 3.4.67 3.4.66 3.4.65 3.4.64 3.4.63 3.4.62 3.4.61 3.4.60 3.4.59 3.4.58 3.4.57 3.4.56 3.4.55 3.4.54 3.4.53 3.4.52 3.4.51 3.4.50 3.4.49 3.4.48 3.4.47 3.4.46 3.4.45 3.4.44 3.4.43 3.4.42 3.4.41 3.4.40 3.4.39 3.4.38 3.4.37 3.4.36 3.4.35 3.4.34 3.4.33 3.4.32 3.4.31 3.4.30 3.4.29 3.4.28 3.4.27 3.4.26 3.4.25 3.4.24 3.4.23 3.4.22 3.4.21 3.4.20 3.4.19 3.4.18 3.4.17 3.4.16 3.4.15 3.4.14 3.4.13 3.4.12 3.4.11 3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.3 3.3.2 3.3.1 3.3.0 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.16 3.0.15 3.0.14 3.0.13 3.0.12 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 3.0.0-rc.46 3.0.0-rc.45 3.0.0-rc.44 3.0.0-rc.43 3.0.0-rc.42 3.0.0-rc.41 3.0.0-rc.40 3.0.0-rc.39 3.0.0-rc.38 3.0.0-rc.37 3.0.0-rc.36 3.0.0-rc.35 3.0.0-rc.34 3.0.0-rc.33 3.0.0-rc.32 3.0.0-rc.31 3.0.0-rc.29 3.0.0-rc.28 3.0.0-rc.27 3.0.0-rc.26 3.0.0-rc.25 3.0.0-rc.24 3.0.0-rc.23 3.0.0-rc.22 3.0.0-rc.21 3.0.0-rc.20 3.0.0-rc.19 3.0.0-rc.18 3.0.0-rc.17 3.0.0-rc.16 3.0.0-rc.15 3.0.0-rc.14 3.0.0-rc.13 3.0.0-rc.12 3.0.0-rc.11 3.0.0-rc.10 3.0.0-rc.9 3.0.0-rc.8 3.0.0-rc.7 3.0.0-rc.6 3.0.0-rc.5 3.0.0-rc.4 3.0.0-rc.3 3.0.0-rc.1 3.0.0-rc.0 2.21.15 2.21.14 2.21.13 2.21.12 2.21.11 2.21.10 2.21.9 2.21.8 2.21.7 2.21.6 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.3 2.20.2 2.20.1 2.20.0 2.19.14 2.19.13 2.19.12 2.19.11 2.19.10 2.19.9 2.19.8 2.19.7 2.19.6 2.19.5 2.19.4 2.19.3 retired 2.19.2 retired 2.19.1 retired 2.19.0 retired 2.18.2 2.18.1 2.18.0 2.17.24 2.17.23 2.17.22 2.17.21 2.17.20 2.17.19 2.17.18 2.17.17 2.17.16 2.17.15 2.17.14 2.17.13 2.17.12 2.17.11 2.17.10 2.17.9 2.17.8 2.17.7 2.17.6 2.17.5 2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.1 2.16.0 2.15.20 2.15.19 2.15.18 2.15.17 2.15.16 2.15.15 2.15.14 2.15.13 2.15.12 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.2 2.15.1 2.15.0 2.14.21 2.14.20 2.14.19 2.14.18 2.14.17 2.14.16 2.14.15 2.14.14 2.14.13 2.14.12 2.14.11 2.14.10 2.14.9 2.14.8 2.14.7 2.14.6 2.14.5 2.14.4 2.14.3 2.14.2 2.14.1 2.14.0 2.13.4 retired 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.11 2.11.10 2.11.9 2.11.8 2.11.7 2.11.6 2.11.5 2.11.4 2.11.3 2.11.2 2.11.1 2.11.0 2.11.0-rc.3 2.11.0-rc.2 2.11.0-rc.1 2.11.0-rc.0 2.10.2 2.10.1 2.10.0 2.9.29 2.9.28 2.9.27 2.9.26 2.9.25 2.9.24 2.9.23 2.9.22 2.9.21 2.9.20 2.9.19 2.9.18 2.9.17 2.9.16 2.9.15 2.9.14 2.9.13 2.9.12 2.9.11 2.9.10 2.9.9 2.9.8 2.9.7 2.9.6 2.9.5 2.9.4 2.9.3 2.9.2 2.9.1 2.9.0 2.8.1 2.8.0 2.7.1 2.7.0 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.5.0-rc.6 2.5.0-rc.5 2.5.0-rc.4 2.5.0-rc.3 2.5.0-rc.2 2.5.0-rc.1 2.5.0-rc.0 2.4.30 2.4.29 2.4.28 2.4.27 2.4.26 2.4.25 2.4.24 2.4.23 2.4.22 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 2.0.0-pre.8 2.0.0-pre.7 2.0.0-pre.6 2.0.0-pre.5 2.0.0-pre.4 2.0.0-pre.3 2.0.0-pre.2 2.0.0-pre.1 2.0.0-pre.0 1.53.3 1.53.2 1.53.0 1.52.0-rc.22 1.52.0-rc.21 1.52.0-rc.20 1.52.0-rc.19 1.52.0-rc.18 1.52.0-rc.17 1.52.0-rc.16 1.52.0-rc.15 1.52.0-rc.14 1.52.0-rc.13 1.52.0-rc.12 1.52.0-rc.11 1.52.0-rc.10 1.52.0-rc.9 1.52.0-rc.8 1.52.0-rc.7 1.52.0-rc.6 1.52.0-rc.5 1.52.0-rc.4 1.52.0-rc.3 1.52.0-rc.2 1.52.0-rc.1 1.52.0-rc.0 1.51.2 1.51.1 retired 1.51.0 1.50.21 1.50.20 1.50.19 1.50.18 1.50.17 1.50.16 1.50.15 1.50.14 1.50.13 1.50.12 1.50.11 1.50.10 1.50.9 1.50.8 1.50.7 1.50.6 1.50.5 1.50.4 1.50.3 1.50.2 1.50.1 1.50.0 1.49.0 1.48.0-rc.30 1.48.0-rc.29 1.48.0-rc.28 1.48.0-rc.27 1.48.0-rc.26 1.48.0-rc.25 1.48.0-rc.24 1.48.0-rc.23 1.48.0-rc.22 1.48.0-rc.21 1.48.0-rc.20 1.48.0-rc.19 1.48.0-rc.18 1.48.0-rc.17 1.48.0-rc.16 1.48.0-rc.15 1.48.0-rc.14 1.48.0-rc.13 1.48.0-rc.12 1.48.0-rc.11 1.48.0-rc.10 1.48.0-rc.9 1.48.0-rc.8 1.48.0-rc.7 1.48.0-rc.6 1.48.0-rc.5 1.48.0-rc.4 1.48.0-rc.3 1.48.0-rc.2 1.48.0-rc.1 1.48.0-rc.0 1.47.12 1.47.11 1.47.10 1.47.9 1.47.8 1.47.7 1.47.6 1.47.5 1.47.4 1.47.3 1.47.2 1.47.1 1.47.0 1.46.13 1.46.12 1.46.11 1.46.10 1.46.9 1.46.8 1.46.7 1.46.6 1.46.5 1.46.4 1.46.3 1.46.2 1.46.1 1.46.0 1.45.0-rc9 1.45.0-rc8 1.45.0-rc7 1.45.0-rc6 1.45.0-rc5 1.45.0-rc4 1.45.0-rc3 1.45.0-rc20 1.45.0-rc2 1.45.0-rc19 1.45.0-rc18 1.45.0-rc17 1.45.0-rc16 1.45.0-rc15 1.45.0-rc14 1.45.0-rc13 1.45.0-rc12 1.45.0-rc11 1.45.0-rc10 1.45.0-rc1 1.45.0-rc0 1.44.13 1.44.12 1.44.11 1.44.10 1.44.9 1.44.8 1.44.7 1.44.6 1.44.5 1.44.4 1.44.3 1.44.2 1.44.1 1.44.0 1.43.12 1.43.11 1.43.10 1.43.9 1.43.8 1.43.7 1.43.6 1.43.5 1.43.4 1.43.3 1.43.2 1.43.1 1.43.0 1.42.0 1.41.12 1.41.11 1.41.10 1.41.9 1.41.8 1.41.7 1.41.6 1.41.5 1.41.4 1.41.3 1.41.2 1.41.1 1.41.0 1.40.0 1.39.7 1.39.6 1.39.5 1.39.4 1.39.3 1.39.2 1.39.1 1.39.0 1.38.0 1.37.2 1.37.1 1.37.0 1.36.22 1.36.21 1.36.19 1.36.18 1.36.17 1.36.16 1.36.15 1.36.14 1.36.13 1.36.12 1.36.11 1.36.10 1.36.9 1.36.8 1.36.7 1.36.6 1.36.5 1.36.4 1.36.3 1.36.2 1.36.0 1.35.1 1.35.0 1.34.9 1.34.8 1.34.7 1.34.6 1.34.5 1.34.4 1.34.3 1.34.2 1.34.1 1.34.0 1.33.0 1.32.2 1.32.1 1.32.0 1.31.1 1.31.0 1.30.2 1.30.1 1.29.0-rc1 1.29.0-rc0 1.28.1 1.28.0 1.27.1 1.27.0 1.26.13 1.26.12 1.26.11 1.26.10 1.26.9 1.26.8 1.26.7 1.26.6 1.26.5 1.26.4 1.26.2 1.26.1 1.26.0 1.25.8 1.25.7 1.25.6 1.25.5 1.25.4 1.25.3 1.25.2 1.25.1 1.25.0 1.24.2 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.1 1.22.0 1.20.1 1.20.0 1.19.1 1.19.0 1.18.1 1.18.0 1.17.1 1.17.0 1.16.2 1.15.1 1.15.0 1.14.0 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.0 1.11.1 1.11.0 1.10.0 1.9.0 1.8.0 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.1 1.5.0 1.4.1 1.4.0 1.3.1 1.3.0 1.2.1 1.2.0 1.1.3 1.1.2 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 0.13.1 0.13.0 0.12.0 0.10.0 0.9.1 0.9.0 0.8.0 0.7.0 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.0 0.3.0 0.2.0 0.1.9 0.1.8 0.1.3 0.1.1 0.1.0

A declarative, extensible framework for building Elixir applications.

Security advisory: This version has known vulnerabilities. View advisories

Current section

Files

Jump to
ash lib ash policy policy.ex
Raw

lib/ash/policy/policy.ex

defmodule Ash.Policy.Policy do
@moduledoc "Represents a policy on an Ash.Resource"
# For now we just write to `checks` and move them to `policies`
# on build, when we support nested policies we can change that.
defstruct [
:condition,
:policies,
:bypass?,
:description,
:access_type
]
@type t :: %__MODULE__{}
@static_checks [
{Ash.Policy.Check.Static, [result: true]},
{Ash.Policy.Check.Static, [result: false]},
true,
false
]
def solve(authorizer) do
authorizer = strict_check_all_conditions(authorizer)
policies = policies_that_may_apply(authorizer)
{expression, authorizer} =
build_requirements_expression(policies, authorizer)
case expression do
expr when is_boolean(expr) ->
{:ok, expr, authorizer}
expression ->
Ash.Policy.SatSolver.solve(expression, fn scenario, bindings ->
scenario
|> Ash.SatSolver.solutions_to_predicate_values(bindings)
|> Map.drop(@static_checks)
end)
|> case do
{:ok, scenarios} ->
{:ok, scenarios, authorizer}
{:error, error} ->
{:error, authorizer, error}
end
end
catch
{:error, authorizer, error} ->
{:error, authorizer, error}
end
defp strict_check_all_conditions(authorizer) do
Enum.reduce(authorizer.policies || [], authorizer, fn policy, authorizer ->
Enum.reduce_while(policy.condition || [], authorizer, fn condition, authorizer ->
case fetch_or_strict_check_fact(authorizer, condition) do
{:ok, true, authorizer} -> {:cont, authorizer}
{:ok, _, authorizer} -> {:halt, authorizer}
{:error, authorizer} -> {:halt, authorizer}
end
end)
end)
end
defp policies_that_may_apply(authorizer) do
Enum.filter(authorizer.policies || [], fn policy ->
Enum.all?(policy.condition || [], fn condition ->
case fetch_fact(authorizer.facts, condition) do
{:ok, true} -> true
{:ok, false} -> false
{:ok, :unknown} -> true
:error -> true
end
end)
end)
end
@doc false
def transform(policy) do
cond do
Enum.empty?(policy.policies) ->
{:error, "Policies must have at least one check."}
policy.bypass? &&
Enum.all?(List.wrap(policy.policies), &(&1.type in [:forbid_if, :forbid_unless])) ->
{:error,
"""
Bypass policies that can only ever forbid have no effect.
When a bypass is authorized, it skips all remaining policies (including other bypasses)
and authorizes the request. If it fails, it is ignored and the remaining policies are checked.
This policy only contains `forbid_if` or `forbid_unless` check types therefore, it can
never have an effect.
"""}
policy.condition in [nil, []] ->
{:ok, %{policy | condition: [{Ash.Policy.Check.Static, result: true}]}}
true ->
{:ok, policy}
end
end
defp build_requirements_expression(policies, authorizer) do
at_least_one_policy_expression =
at_least_one_policy_expression(policies)
policy_expression =
compile_policy_expression(policies)
policy_and_condition_expression =
{:and, at_least_one_policy_expression, policy_expression}
authorizer = %{authorizer | solver_statement: policy_and_condition_expression}
facts_expression =
authorizer.facts
|> Map.drop([true, false])
|> Ash.Policy.SatSolver.facts_to_statement()
if is_nil(facts_expression) do
handle_constants(policy_and_condition_expression, authorizer)
else
handle_constants({:and, facts_expression, policy_and_condition_expression}, authorizer)
end
end
# at least one policy must apply
# or one bypass must authorize
def at_least_one_policy_expression(policies) do
policies
|> List.wrap()
|> Enum.reduce(false, fn policy, condition_exprs ->
if policy.bypass? do
if condition_exprs == false do
compile_policy_expression([policy])
else
{:or, compile_policy_expression([policy]), condition_exprs}
end
else
if condition_exprs == false do
condition_expression(policy.condition)
else
{:or, condition_expression(policy.condition), condition_exprs}
end
end
end)
end
def fetch_or_strict_check_fact(authorizer, %{check_module: mod, check_opts: opts}) do
fetch_or_strict_check_fact(authorizer, {mod, opts})
end
def fetch_or_strict_check_fact(authorizer, {Ash.Policy.Check.Static, opts}) do
{:ok, opts[:result], authorizer}
end
def fetch_or_strict_check_fact(authorizer, {check_module, opts}) do
Enum.find_value(authorizer.facts, fn
{{fact_mod, fact_opts}, result} when result != :unknown ->
if check_module == fact_mod &&
Keyword.drop(fact_opts, [:access_type, :ash_field_policy?]) ==
Keyword.drop(opts, [:access_type, :ash_field_policy?]) do
{:ok, result}
end
_ ->
nil
end)
|> case do
nil ->
if check_module.requires_original_data?(authorizer, opts) &&
missing_original_data?(authorizer) do
throw(
{:error, authorizer,
Ash.Error.Forbidden.InitialDataRequired.exception(
source: "check: #{check_module.describe(opts)} requires initial data"
)}
)
else
case check_module.strict_check(authorizer.actor, authorizer, opts) do
{:ok, value} when is_boolean(value) or value == :unknown ->
authorizer = %{
authorizer
| facts: Map.put(authorizer.facts, {check_module, opts}, value)
}
if value == :unknown do
{:error, authorizer}
else
{:ok, value, authorizer}
end
{:error, error} ->
throw({:error, authorizer, Ash.Error.to_ash_error(error)})
end
end
{:ok, :unknown} ->
{:error, authorizer}
{:ok, value} ->
{:ok, value, authorizer}
end
end
defp missing_original_data?(%{
changeset: %Ash.Changeset{data: %Ash.Changeset.OriginalDataNotAvailable{}}
}) do
true
end
defp missing_original_data?(_), do: false
def fetch_fact(facts, %{check_module: mod, check_opts: opts}) do
fetch_fact(facts, {mod, opts})
end
def fetch_fact(_facts, {Ash.Policy.Check.Static, opts}) do
{:ok, opts[:result]}
end
def fetch_fact(facts, {mod, opts}) do
Enum.find_value(facts, fn
{{fact_mod, fact_opts}, result} ->
if mod == fact_mod &&
Keyword.drop(fact_opts, [:access_type, :ash_field_policy?]) ==
Keyword.drop(opts, [:access_type, :ash_field_policy?]) do
{:ok, result}
end
_ ->
nil
end)
|> case do
nil ->
:error
:unknown ->
:error
value ->
value
end
end
defp condition_expression(condition) do
condition
|> List.wrap()
|> Enum.reduce(true, fn condition, expression ->
if expression == true do
condition
else
{:and, condition, expression}
end
end)
end
defp compile_policy_expression([]) do
false
end
defp compile_policy_expression(nil) do
false
end
defp compile_policy_expression([%struct{condition: condition, policies: policies}])
when struct in [__MODULE__, Ash.Policy.FieldPolicy] do
condition_expression = condition_expression(condition)
compiled_policies = compile_policy_expression(policies)
{:or, {:and, condition_expression, compiled_policies}, {:not, condition_expression}}
end
defp compile_policy_expression([
%{condition: condition, policies: policies, bypass?: bypass?} | rest
]) do
condition_expression = condition_expression(condition)
if bypass? do
policy_expression = compile_policy_expression(policies)
condition_and_policy_expression = {:and, condition_expression, policy_expression}
rest = compile_policy_expression(rest)
{:or, condition_and_policy_expression, rest}
else
policy_expression = compile_policy_expression(policies)
rest_expr = compile_policy_expression(rest)
{:and, {:or, {:not, condition_expression}, {:and, condition_expression, policy_expression}},
rest_expr}
end
end
defp compile_policy_expression([%{type: :authorize_if} = clause]) do
{clause.check_module, clause.check_opts}
end
defp compile_policy_expression([%{type: :authorize_if} = clause | rest]) do
{:or, {clause.check_module, clause.check_opts}, compile_policy_expression(rest)}
end
defp compile_policy_expression([%{type: :authorize_unless} = clause]) do
{:not, {clause.check_module, clause.check_opts}}
end
defp compile_policy_expression([%{type: :authorize_unless} = clause | rest]) do
rest = compile_policy_expression(rest)
{:or, {:not, {clause.check_module, clause.check_opts}}, rest}
end
defp compile_policy_expression([%{type: :forbid_if}]) do
false
end
defp compile_policy_expression([%{type: :forbid_if} = clause | rest]) do
{:and, {:not, {clause.check_module, clause.check_opts}}, compile_policy_expression(rest)}
end
defp compile_policy_expression([%{type: :forbid_unless}]) do
false
end
defp compile_policy_expression([%{type: :forbid_unless} = clause | rest]) do
{:and, {clause.check_module, clause.check_opts}, compile_policy_expression(rest)}
end
@doc false
def debug_expr(expr, label \\ "Expr") do
expr
|> clean_constant_checks()
|> do_debug_expr()
|> Macro.to_string()
|> then(&"#{label}: \n\n #{&1}")
end
defp clean_constant_checks({combinator, left, right}) when combinator in [:and, :or] do
left = clean_constant_checks(left)
right = clean_constant_checks(right)
case {left, right} do
{{Ash.Policy.Check.Static, left_opts}, {Ash.Policy.Check.Static, right_opts}} ->
if left_opts[:result] && right_opts[:result] do
{Ash.Policy.Check.Static, Keyword.put(left_opts, :result, true)}
else
{combinator, left, right}
end
{{Ash.Policy.Check.Static, left_opts}, right} ->
if left_opts[:result] do
right
else
{combinator, left, right}
end
{left, {Ash.Policy.Check.Static, right_opts}} ->
if right_opts[:result] do
left
else
{combinator, left, right}
end
{left, right} ->
{combinator, left, right}
end
end
defp clean_constant_checks({:not, expr}) do
case clean_constant_checks(expr) do
{Ash.Policy.Check.Static, opts} ->
{Ash.Policy.Check.Static, Keyword.put(opts, :result, !opts[:result])}
other ->
{:not, other}
end
end
defp clean_constant_checks(other), do: other
defp handle_constants({:and, l, r}, authorizer) do
{l, authorizer} = handle_constants(l, authorizer)
if l == false do
{false, authorizer}
else
{r, authorizer} = handle_constants(r, authorizer)
case {l, r} do
{_, false} -> {false, authorizer}
{false, _} -> {false, authorizer}
{true, true} -> {true, authorizer}
{true, r} -> {r, authorizer}
{l, true} -> {l, authorizer}
{l, r} -> {{:and, l, r}, authorizer}
end
end
end
defp handle_constants({:or, l, r}, authorizer) do
{l, authorizer} = handle_constants(l, authorizer)
if l == true do
{true, authorizer}
else
{r, authorizer} = handle_constants(r, authorizer)
case {l, r} do
{_, true} -> {true, authorizer}
{true, _} -> {true, authorizer}
{false, false} -> {false, authorizer}
{false, r} -> {r, authorizer}
{l, false} -> {l, authorizer}
{l, r} -> {{:or, l, r}, authorizer}
end
end
end
defp handle_constants({:not, l}, authorizer) do
case handle_constants(l, authorizer) do
{true, authorizer} -> {false, authorizer}
{false, authorizer} -> {true, authorizer}
{l, authorizer} -> {{:not, l}, authorizer}
end
end
defp handle_constants({mod, opts}, authorizer) do
case fetch_or_strict_check_fact(authorizer, {mod, opts}) do
{:ok, result, authorizer} ->
{result, authorizer}
{:error, authorizer} ->
{{mod, opts}, authorizer}
end
end
defp handle_constants(other, authorizer), do: {other, authorizer}
defp do_debug_expr({:and, l, r}) do
quote do
unquote(do_debug_expr(l)) and unquote(do_debug_expr(r))
end
end
defp do_debug_expr({:or, l, r}) do
quote do
unquote(do_debug_expr(l)) or unquote(do_debug_expr(r))
end
end
defp do_debug_expr({:not, v}) do
quote do
not unquote(do_debug_expr(v))
end
end
defp do_debug_expr({check_module, check_opts}) do
check_module.describe(check_opts)
end
defp do_debug_expr(v) do
quote do
unquote(Macro.escape(v))
end
end
end