improvement: add more authorizer state management

Added more opportunities for authorizers to pass back state.
This is being used to ensure that ash policy authorizer errors
can always have enough information to provide a policy breakdown
This commit is contained in:
Zach Daniel 2021-12-21 12:24:12 -05:00
parent 6b95dec339
commit ce3ae44a4c
3 changed files with 37 additions and 9 deletions

View file

@ -36,7 +36,11 @@ defmodule Ash.Authorizer do
if function_exported?(module, :exception, 2) do
module.exception(reason, state)
else
Ash.Error.Forbidden.exception([])
if reason == :must_pass_strict_check do
Ash.Error.Forbidden.MustPassStrictCheck.exception([])
else
Ash.Error.Forbidden.exception([])
end
end
end

View file

@ -38,7 +38,6 @@ defmodule Ash.Engine.Request do
@type t :: %__MODULE__{}
alias Ash.Authorizer
alias Ash.Error.Forbidden.MustPassStrictCheck
alias Ash.Error.Invalid.{DuplicatedPath, ImpossiblePath}
require Ash.Query
@ -504,6 +503,21 @@ defmodule Ash.Engine.Request do
:authorized ->
{:ok, set_authorizer_state(request, authorizer, :authorized), notifications, []}
{:filter, authorizer_state, filter} ->
request
|> set_authorizer_state(authorizer, authorizer_state)
|> apply_filter(authorizer, filter, true)
|> case do
{:ok, request} ->
{:ok, request, notifications, []}
{:ok, request, new_notifications, deps} ->
{:ok, request, new_notifications ++ notifications, deps}
other ->
other
end
{:filter, filter} ->
request
|> apply_filter(authorizer, filter, true)
@ -518,8 +532,13 @@ defmodule Ash.Engine.Request do
other
end
{:filter_and_continue, _, _} when strict_check_only? ->
{:error, MustPassStrictCheck.exception(resource: request.resource)}
{:filter_and_continue, _, authorizer_state} when strict_check_only? ->
{:error,
Authorizer.exception(
authorizer,
:must_pass_strict_check,
authorizer_state
)}
{:filter_and_continue, filter, new_authorizer_state} ->
request
@ -536,8 +555,13 @@ defmodule Ash.Engine.Request do
other
end
{:continue, _} when strict_check_only? ->
{:error, MustPassStrictCheck.exception(resource: request.resource)}
{:continue, authorizer_state} when strict_check_only? ->
{:error,
Authorizer.exception(
authorizer,
:must_pass_strict_check,
authorizer_state
)}
{:continue, authorizer_state} ->
{:ok, set_authorizer_state(request, authorizer, authorizer_state), notifications, []}

View file

@ -2,15 +2,15 @@ defmodule Ash.Error.Forbidden.MustPassStrictCheck do
@moduledoc "Used when unreachable code/conditions are reached in the framework"
use Ash.Error.Exception
def_ash_error([:resource], class: :forbidden)
def_ash_error([], class: :forbidden)
defimpl Ash.ErrorKind do
def id(_), do: Ash.UUID.generate()
def code(_), do: "must_pass_strict_check"
def message(%{resource: resource}) do
"A request against #{inspect(resource)} was required to pass strict check, but it did not"
def message(_) do
"The request was required to pass strict check, but it did not"
end
end
end