Skip to content

Commit

Permalink
improvement: add more authorizer state management
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zachdaniel committed Dec 21, 2021
1 parent 6b95dec commit ce3ae44
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
6 changes: 5 additions & 1 deletion lib/ash/authorizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
34 changes: 29 additions & 5 deletions lib/ash/engine/request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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, []}
Expand Down
6 changes: 3 additions & 3 deletions lib/ash/error/forbidden/must_pass_strict_check.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit ce3ae44

Please sign in to comment.