mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 21:13:10 +12:00
fix: fix pattern match error on certain policy conditions
improvement: various policy behavior cleanups
This commit is contained in:
parent
b428fb8995
commit
2cd6360379
5 changed files with 45 additions and 48 deletions
|
@ -489,6 +489,10 @@ defmodule Ash.Policy.Authorizer do
|
|||
log_successful_policy_breakdown(authorizer, filter)
|
||||
{:filter, strict_check_all_facts(authorizer), filter}
|
||||
|
||||
{:filter_and_continue, filter, authorizer} ->
|
||||
log_successful_policy_breakdown(authorizer, filter)
|
||||
{:filter, strict_check_all_facts(authorizer), filter}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
||||
|
@ -959,7 +963,7 @@ defmodule Ash.Policy.Authorizer do
|
|||
match?(
|
||||
{:ok, _},
|
||||
Ash.Policy.Policy.fetch_fact(authorizer.facts, {check_module, opts})
|
||||
) || check_module.type() == :filter
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ defmodule Ash.Policy.Checker do
|
|||
|
||||
{:ok, scenarios, authorizer} ->
|
||||
scenarios
|
||||
|> Ash.Policy.SatSolver.simplify_clauses()
|
||||
|> remove_scenarios_with_impossible_facts(authorizer)
|
||||
|> Ash.Policy.SatSolver.simplify_clauses()
|
||||
|> case do
|
||||
[] -> {:ok, false, authorizer}
|
||||
scenarios -> {:ok, scenarios, authorizer}
|
||||
|
@ -99,16 +99,18 @@ defmodule Ash.Policy.Checker do
|
|||
end
|
||||
|
||||
defp remove_scenarios_with_impossible_facts(scenarios, authorizer) do
|
||||
# Remove any scenarios with a fact that must be a certain value, but are not, at strict check time
|
||||
# They aren't true, so that scenario isn't possible
|
||||
|
||||
Enum.reject(scenarios, fn scenario ->
|
||||
Enum.any?(scenario, fn {{mod, opts}, required_value} ->
|
||||
opts[:access_type] == :strict &&
|
||||
not match?(
|
||||
{:ok, ^required_value},
|
||||
Policy.fetch_fact(authorizer.facts, {mod, opts})
|
||||
)
|
||||
case Policy.fetch_fact(authorizer.facts, {mod, opts}) do
|
||||
{:ok, :unknown} ->
|
||||
opts[:access_type] == :strict
|
||||
|
||||
{:ok, value} ->
|
||||
value != required_value
|
||||
|
||||
:error ->
|
||||
opts[:access_type] == :strict
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -443,13 +443,13 @@ defmodule Ash.Policy.Policy do
|
|||
{condition_and_policy_expression, authorizer} =
|
||||
case compile_policy_expression(policies, authorizer) do
|
||||
{true, authorizer} ->
|
||||
{condition, authorizer}
|
||||
{condition_expression, authorizer}
|
||||
|
||||
{false, authorizer} ->
|
||||
{false, authorizer}
|
||||
|
||||
{policy_expression, authorizer} ->
|
||||
{{:and, condition, policy_expression}, authorizer}
|
||||
{{:and, condition_expression, policy_expression}, authorizer}
|
||||
end
|
||||
|
||||
case condition_and_policy_expression do
|
||||
|
|
|
@ -51,36 +51,33 @@ defmodule Ash.Policy.SatSolver do
|
|||
def simplify_clauses([scenario]), do: [scenario]
|
||||
|
||||
def simplify_clauses(scenarios) do
|
||||
unnecessary_clauses =
|
||||
scenarios
|
||||
|> Enum.with_index()
|
||||
|> Enum.flat_map(fn {scenario, index} ->
|
||||
scenario
|
||||
|> Enum.flat_map(fn {fact, _value} ->
|
||||
if Enum.find(scenarios, fn other_scenario ->
|
||||
scenario_makes_fact_irrelevant?(other_scenario, scenario, fact)
|
||||
end) do
|
||||
[fact]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
|> Enum.map(fn fact ->
|
||||
{index, fact}
|
||||
end)
|
||||
end)
|
||||
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|
||||
indexed = Enum.with_index(scenarios)
|
||||
|
||||
case unnecessary_clauses do
|
||||
empty when empty == %{} ->
|
||||
indexed
|
||||
|> Enum.find_value(fn {scenario, index} ->
|
||||
Enum.find_value(scenario, fn {fact, _value} ->
|
||||
case Enum.find_value(indexed, fn {other_scenario, other_index} ->
|
||||
if scenario != other_scenario &&
|
||||
scenario_makes_fact_irrelevant?(other_scenario, scenario, fact) do
|
||||
other_index
|
||||
end
|
||||
end) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
other_index ->
|
||||
{fact, other_index, index}
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|> case do
|
||||
nil ->
|
||||
scenarios
|
||||
|
||||
unnecessary_clauses ->
|
||||
unnecessary_clauses
|
||||
|> Enum.reduce(scenarios, fn {index, facts}, scenarios ->
|
||||
List.update_at(scenarios, index, &Map.drop(&1, facts))
|
||||
end)
|
||||
|> Enum.reject(&(&1 == %{}))
|
||||
{fact, index1, index2} ->
|
||||
scenarios
|
||||
|> List.update_at(index1, &Map.delete(&1, fact))
|
||||
|> List.update_at(index2, &Map.delete(&1, fact))
|
||||
|> Enum.uniq()
|
||||
|> simplify_clauses()
|
||||
end
|
||||
|
@ -91,18 +88,12 @@ defmodule Ash.Policy.SatSolver do
|
|||
do: false
|
||||
|
||||
def scenario_makes_fact_irrelevant?(potential_irrelevant_maker, scenario, fact) do
|
||||
scenario_is_subset?(Map.delete(potential_irrelevant_maker, fact), scenario) &&
|
||||
Map.delete(potential_irrelevant_maker, fact) == Map.delete(scenario, fact) &&
|
||||
Map.has_key?(potential_irrelevant_maker, fact) && Map.has_key?(scenario, fact) &&
|
||||
Map.get(potential_irrelevant_maker, fact) !=
|
||||
Map.get(scenario, fact)
|
||||
end
|
||||
|
||||
defp scenario_is_subset?(left, right) do
|
||||
Enum.all?(left, fn {fact, value} ->
|
||||
Map.get(right, fact) == value
|
||||
end)
|
||||
end
|
||||
|
||||
@spec add_negations_and_solve(term, term) :: term | no_return()
|
||||
defp add_negations_and_solve(cnf, negations) do
|
||||
solve_expression(cnf ++ negations)
|
||||
|
|
|
@ -11,13 +11,13 @@ defmodule Ash.Test.Support.PolicyComplex.Post do
|
|||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action_type(:read) do
|
||||
policy [action_type(:read)] do
|
||||
authorize_if relates_to_actor_via(:author)
|
||||
authorize_if relates_to_actor_via([:author, :friends])
|
||||
end
|
||||
|
||||
policy action_type(:create) do
|
||||
authorize_if relating_to_actor(:author)
|
||||
authorize_if always()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue