fix: don't attempt to re-authorize access to already retrieved records in Api.load/2

improvement: show conditions in policy breakdowns
This commit is contained in:
Zach Daniel 2022-10-20 13:00:23 -04:00
parent 8092cd899a
commit d1f05b6c42
5 changed files with 118 additions and 24 deletions

View file

@ -236,6 +236,13 @@ defmodule Ash.Actions.Read do
fn %{authorize?: authorize?, actor: actor} = context ->
query_opts = query_opts(request_opts, context)
authorize? =
if Keyword.has_key?(request_opts, :initial_data) do
request_opts[:authorize?]
else
authorize?
end
input = query_input.(context) || %{}
tenant =
@ -345,7 +352,7 @@ defmodule Ash.Actions.Read do
end
end
),
authorize?: true,
authorize?: !Keyword.has_key?(request_opts, :initial_data),
data: data_field(request_opts, path),
path: path ++ [:fetch],
async?: !Keyword.has_key?(request_opts, :initial_data),

View file

@ -158,27 +158,104 @@ defmodule Ash.Error.Forbidden.Policy do
""
end
{description, state} = describe_checks(policy.policies, facts)
{condition_description, applies} = describe_conditions(policy.condition, facts)
tag =
case state do
:unknown ->
""
if applies == true do
{description, state} = describe_checks(policy.policies, facts)
:authorized ->
"🌟"
tag =
case state do
:unknown ->
""
:forbidden ->
""
end
:authorized ->
"🌟"
title(Enum.map(description, &[" ", &1]), [
" ",
bypass,
policy.description || "Policy",
" | ",
tag
])
:forbidden ->
""
end
title(
[Enum.map(condition_description, &[" ", &1]), Enum.map(description, &[" ", &1])],
[
" ",
bypass,
policy.description || "Policy",
" | ",
tag
]
)
else
tag =
if applies == false do
"⬇️"
else
"?"
end
title(
Enum.map(condition_description, &[" ", &1]),
[" ", bypass, policy.description || "Policy", " | ", tag]
)
end
end
defp describe_conditions(condition, facts) do
condition
|> List.wrap()
|> case do
[{Ash.Policy.Check.Static, opts}] ->
{[], opts[:result]}
conditions ->
conditions
|> Enum.reduce({[], true}, fn condition, {conditions, status} ->
{mod, opts} =
case condition do
%{module: module, opts: opts} ->
{module, opts}
{module, opts} ->
{module, opts}
end
new_status =
if status in [false, :unknown] do
false
else
case Policy.fetch_fact(facts, {mod, opts}) do
{:ok, true} ->
true
{:ok, false} ->
false
_ ->
:unknown
end
end
{[["condition: ", mod.describe(opts)] | conditions], new_status}
end)
|> then(fn {conditions, status} ->
conditions =
conditions
|> Enum.reverse()
|> case do
[] ->
[]
conditions ->
[
conditions
|> Enum.intersperse("\n"),
"\n"
]
end
{conditions, status}
end)
end
end
defp describe_checks(checks, facts) do

View file

@ -947,10 +947,12 @@ defmodule Ash.Filter do
resource: query.resource,
api: api,
query:
Ash.Query.set_context(query, %{
query
|> Ash.Query.set_context(%{
filter_only?: true,
filter_references: refs[path] || []
}),
})
|> Ash.Query.select([]),
async?: false,
path: request_path ++ [:filter, path],
strict_check_only?: true,

View file

@ -97,4 +97,16 @@ defmodule Ash.Test.Policy.ComplexTest do
|> Ash.Query.filter(count_of_comments == 10)
|> Api.read!(actor: me)
end
test "data can be loaded without forbidden errors from selecting", %{me: me} do
users =
Ash.Test.Support.PolicyComplex.User
|> Ash.Query.deselect(:private_email)
|> Api.read!(actor: me)
Application.put_env(:foo, :bar, true)
users
|> Api.load!([:posts], actor: me, authorize?: true)
end
end

View file

@ -57,10 +57,6 @@ defmodule Ash.Test.Support.PolicyComplex.User do
end
end
preparations do
prepare build(deselect: :private_email)
end
code_interface do
define_for Ash.Test.Support.PolicyComplex.Api
define :create, args: [:name]