mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
improvement: support authorize?
as a changeset option
This commit is contained in:
parent
a4770e35fd
commit
b9675295e6
13 changed files with 291 additions and 182 deletions
39
lib/ash.ex
39
lib/ash.ex
|
@ -14,13 +14,14 @@ defmodule Ash do
|
|||
def get_context_for_transfer do
|
||||
context = Ash.get_context()
|
||||
actor = Process.get(:ash_actor)
|
||||
tenant = Process.get(:tenant)
|
||||
authorize? = Process.get(:ash_authorize?)
|
||||
tenant = Process.get(:ash_tenant)
|
||||
|
||||
%{context: context, actor: actor, tenant: tenant}
|
||||
%{context: context, actor: actor, tenant: tenant, authorize?: authorize?}
|
||||
end
|
||||
|
||||
@spec transfer_context(term) :: :ok
|
||||
def transfer_context(%{context: context, actor: actor, tenant: tenant}) do
|
||||
def transfer_context(%{context: context, actor: actor, tenant: tenant, authorize?: authorize?}) do
|
||||
case actor do
|
||||
{:actor, actor} ->
|
||||
Ash.set_actor(actor)
|
||||
|
@ -37,6 +38,14 @@ defmodule Ash do
|
|||
:ok
|
||||
end
|
||||
|
||||
case authorize? do
|
||||
{:authorize?, authorize?} ->
|
||||
Ash.set_authorize?(authorize?)
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
|
||||
Ash.set_context(context)
|
||||
end
|
||||
|
||||
|
@ -60,6 +69,16 @@ defmodule Ash do
|
|||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets authorize? into the process dictionary that is used for all changesets and queries.
|
||||
"""
|
||||
@spec set_authorize?(map) :: :ok
|
||||
def set_authorize?(map) do
|
||||
Process.put(:ash_authorize?, {:authorize?, map})
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the current actor from the process dictionary
|
||||
"""
|
||||
|
@ -74,6 +93,20 @@ defmodule Ash do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the current authorize? from the process dictionary
|
||||
"""
|
||||
@spec get_authorize?() :: term()
|
||||
def get_authorize? do
|
||||
case Process.get(:ash_authorize?) do
|
||||
{:authorize?, value} ->
|
||||
value
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets tenant into the process dictionary that is used for all changesets and queries.
|
||||
"""
|
||||
|
|
|
@ -13,15 +13,6 @@ defmodule Ash.Actions.Create do
|
|||
def run(api, changeset, action, opts) do
|
||||
{changeset, opts} = Ash.Actions.Helpers.add_process_context(api, changeset, opts)
|
||||
|
||||
opts =
|
||||
case Map.fetch(changeset.context[:private] || %{}, :actor) do
|
||||
{:ok, actor} ->
|
||||
Keyword.put_new(opts, :actor, actor)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
|
||||
upsert? = opts[:upsert?] || get_in(changeset.context, [:private, :upsert?]) || false
|
||||
authorize? = authorize?(opts)
|
||||
upsert_keys = opts[:upsert_keys]
|
||||
|
@ -142,7 +133,8 @@ defmodule Ash.Actions.Create do
|
|||
resource: resource,
|
||||
error_path: error_path,
|
||||
changeset:
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor} = context ->
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor, authorize?: authorize?} =
|
||||
context ->
|
||||
input = changeset_input.(context) || %{}
|
||||
|
||||
tenant =
|
||||
|
@ -163,11 +155,13 @@ defmodule Ash.Actions.Create do
|
|||
resource
|
||||
|> Ash.Changeset.for_create(action.name, input,
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
tenant: tenant,
|
||||
timeout: timeout
|
||||
)
|
||||
|> changeset(api, action,
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
tenant: tenant,
|
||||
timeout: timeout
|
||||
)
|
||||
|
@ -175,6 +169,7 @@ defmodule Ash.Actions.Create do
|
|||
changeset ->
|
||||
changeset(changeset, api, action,
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
tenant: tenant,
|
||||
timeout: timeout
|
||||
)
|
||||
|
@ -263,7 +258,6 @@ defmodule Ash.Actions.Create do
|
|||
|
||||
result =
|
||||
changeset
|
||||
|> Ash.Changeset.put_context(:private, %{actor: actor})
|
||||
|> Ash.Changeset.before_action(
|
||||
&Ash.Actions.ManagedRelationships.setup_managed_belongs_to_relationships(
|
||||
&1,
|
||||
|
|
|
@ -27,15 +27,6 @@ defmodule Ash.Actions.Destroy do
|
|||
def run(api, %{resource: resource} = changeset, action, opts) do
|
||||
{changeset, opts} = Ash.Actions.Helpers.add_process_context(api, changeset, opts)
|
||||
|
||||
opts =
|
||||
case Map.fetch(changeset.context[:private] || %{}, :actor) do
|
||||
{:ok, actor} ->
|
||||
Keyword.put_new(opts, :actor, actor)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
|
||||
authorize? = authorize?(opts)
|
||||
actor = opts[:actor]
|
||||
verbose? = opts[:verbose?]
|
||||
|
@ -124,7 +115,8 @@ defmodule Ash.Actions.Destroy do
|
|||
action: action,
|
||||
error_path: error_path,
|
||||
changeset:
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor} = context ->
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor, authorize?: authorize?} =
|
||||
context ->
|
||||
input = changeset_input.(context) || %{}
|
||||
|
||||
tenant =
|
||||
|
@ -155,11 +147,13 @@ defmodule Ash.Actions.Destroy do
|
|||
|> Ash.Changeset.for_destroy(action.name, input,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
|> changeset(api, action,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
end
|
||||
|
@ -167,6 +161,7 @@ defmodule Ash.Actions.Destroy do
|
|||
changeset ->
|
||||
changeset(changeset, api, action,
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
tenant: tenant,
|
||||
timeout: timeout
|
||||
)
|
||||
|
@ -223,12 +218,12 @@ defmodule Ash.Actions.Destroy do
|
|||
data:
|
||||
Request.resolve(
|
||||
[path ++ [:data, :data], path ++ [:destroy, :changeset]],
|
||||
fn %{actor: actor} = context ->
|
||||
fn %{actor: actor, authorize?: authorize?} = context ->
|
||||
changeset = get_in(context, path ++ [:destroy, :changeset])
|
||||
record = changeset.data
|
||||
|
||||
changeset
|
||||
|> Ash.Changeset.put_context(:private, %{actor: actor})
|
||||
|> Ash.Changeset.put_context(:private, %{actor: actor, authorize?: authorize?})
|
||||
|> Ash.Changeset.with_hooks(fn changeset ->
|
||||
if action.manual? do
|
||||
{:ok, record}
|
||||
|
|
|
@ -24,25 +24,51 @@ defmodule Ash.Actions.Helpers do
|
|||
private: %{
|
||||
actor: actor
|
||||
}
|
||||
} ->
|
||||
}
|
||||
when not is_nil(actor) ->
|
||||
Keyword.put_new(opts, :actor, actor)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
|
||||
{add_context(query_or_changeset), opts |> add_actor(api) |> add_tenant()}
|
||||
opts =
|
||||
case query_or_changeset.context do
|
||||
%{
|
||||
private: %{
|
||||
authorize?: authorize?
|
||||
}
|
||||
}
|
||||
when is_boolean(authorize?) ->
|
||||
Keyword.put_new(opts, :authorize?, authorize?)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
|
||||
opts = opts |> add_actor(api) |> add_authorize?(api) |> add_tenant()
|
||||
|
||||
query_or_changeset = add_context(query_or_changeset, opts)
|
||||
|
||||
{query_or_changeset, opts}
|
||||
end
|
||||
|
||||
defp add_context(query_or_changeset) do
|
||||
defp add_context(query_or_changeset, opts) do
|
||||
context = Process.get(:ash_context, %{}) || %{}
|
||||
private_context = Map.new(Keyword.take(opts, [:actor, :authorize?]))
|
||||
|
||||
case query_or_changeset do
|
||||
%Ash.Query{} ->
|
||||
Ash.Query.set_context(query_or_changeset, context)
|
||||
query_or_changeset
|
||||
|> Ash.Query.set_context(context)
|
||||
|> Ash.Query.set_context(%{private: private_context})
|
||||
|
||||
%Ash.Changeset{} ->
|
||||
Ash.Changeset.set_context(query_or_changeset, context)
|
||||
query_or_changeset
|
||||
|> Ash.Changeset.set_context(context)
|
||||
|> Ash.Changeset.set_context(%{
|
||||
private: private_context
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,6 +91,29 @@ defmodule Ash.Actions.Helpers do
|
|||
raise Ash.Error.Forbidden.ApiRequiresActor, api: api
|
||||
end
|
||||
|
||||
opts
|
||||
else
|
||||
# The only time api would be nil here is when we call this helper inside of `Changeset.for_*` and `Query.for_read`
|
||||
# meaning this will be run again later with the api, so we skip the validations on the api
|
||||
opts
|
||||
end
|
||||
end
|
||||
|
||||
defp add_authorize?(opts, api) do
|
||||
opts =
|
||||
if Keyword.has_key?(opts, :authorize?) do
|
||||
opts
|
||||
else
|
||||
case Process.get(:ash_authorize?) do
|
||||
{:authorize?, value} when is_boolean(value) ->
|
||||
Keyword.put(opts, :authorize?, value)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
end
|
||||
|
||||
if api do
|
||||
case Ash.Api.authorize(api) do
|
||||
:always ->
|
||||
Keyword.put(opts, :authorize?, true)
|
||||
|
|
|
@ -203,16 +203,16 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|
||||
keys ->
|
||||
relationship.destination
|
||||
|> Ash.Query.for_read(read, input, actor: actor)
|
||||
|> Ash.Query.for_read(read, input,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Query.filter(^keys)
|
||||
|> Ash.Query.do_filter(relationship.filter)
|
||||
|> Ash.Query.sort(relationship.sort)
|
||||
|> Ash.Query.set_context(relationship.context)
|
||||
|> Ash.Query.set_tenant(changeset.tenant)
|
||||
|> api(changeset, relationship).read_one(
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api(changeset, relationship).read_one()
|
||||
|> case do
|
||||
{:ok, nil} ->
|
||||
create_belongs_to_record(
|
||||
|
@ -433,15 +433,12 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.for_create(action_name, input,
|
||||
require?: false,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
relationships: opts[:relationships] || []
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api(changeset, relationship).create(
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
return_notifications?: true
|
||||
)
|
||||
|> api(changeset, relationship).create(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, created, notifications} ->
|
||||
changeset =
|
||||
|
@ -834,17 +831,14 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
{:ok, input}
|
||||
else
|
||||
relationship.destination
|
||||
|> Ash.Query.for_read(read, input, actor: actor)
|
||||
|> Ash.Query.for_read(read, input, actor: actor, authorize?: opts[:authorize?])
|
||||
|> Ash.Query.filter(^keys)
|
||||
|> Ash.Query.do_filter(relationship.filter)
|
||||
|> Ash.Query.sort(relationship.sort)
|
||||
|> Ash.Query.set_context(relationship.context)
|
||||
|> Ash.Query.set_tenant(changeset.tenant)
|
||||
|> Ash.Query.limit(1)
|
||||
|> api(changeset, relationship).read_one(
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api(changeset, relationship).read_one()
|
||||
end
|
||||
|> case do
|
||||
{:ok, found} when not is_nil(found) ->
|
||||
|
@ -933,6 +927,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_create(create_or_update, join_input,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
require?: false
|
||||
)
|
||||
|> maybe_force_change_attribute(
|
||||
|
@ -947,11 +942,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
)
|
||||
|> Ash.Changeset.set_context(join_relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.create(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.create(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, _created, notifications} ->
|
||||
case key do
|
||||
|
@ -993,7 +984,8 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_update(create_or_update, input,
|
||||
relationships: opts[:relationships] || [],
|
||||
actor: actor
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> maybe_force_change_attribute(
|
||||
relationship,
|
||||
|
@ -1002,11 +994,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.update(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.update(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, updated, notifications} ->
|
||||
{:ok, [updated | current_value], notifications, [updated]}
|
||||
|
@ -1049,6 +1037,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.for_create(action_name, input,
|
||||
require?: false,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
relationships: opts[:relationships]
|
||||
)
|
||||
|> maybe_force_change_attribute(
|
||||
|
@ -1058,11 +1047,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api(changeset, relationship).create(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api(changeset, relationship).create(return_notifications?: true)
|
||||
end
|
||||
|
||||
case created do
|
||||
|
@ -1097,16 +1082,13 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_create(action_name, regular_params,
|
||||
require?: false,
|
||||
authorize?: opts[:authorize?],
|
||||
relationships: opts[:relationships],
|
||||
actor: actor
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api(changeset, relationship).create(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api(changeset, relationship).create(return_notifications?: true)
|
||||
end
|
||||
|
||||
case created do
|
||||
|
@ -1118,6 +1100,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_create(join_action_name, join_params,
|
||||
require?: false,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> maybe_force_change_attribute(
|
||||
|
@ -1132,11 +1115,7 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
)
|
||||
|> Ash.Changeset.set_context(join_relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api(changeset, relationship).create(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api(changeset, relationship).create(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, _join_row, notifications} ->
|
||||
{:ok, [created | current_value], regular_notifications ++ notifications,
|
||||
|
@ -1232,11 +1211,12 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_update(action_name, input,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
relationships: opts[:relationships] || []
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.update(actor: actor, authorize?: opts[:authorize?], return_notifications?: true)
|
||||
|> api.update(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, updated, update_notifications} ->
|
||||
{:ok, [updated | current_value], update_notifications, [match]}
|
||||
|
@ -1262,11 +1242,12 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_update(action_name, regular_params,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?],
|
||||
relationships: opts[:relationships]
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.update(actor: actor, authorize?: opts[:authorize?], return_notifications?: true)
|
||||
|> api.update(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, updated, update_notifications} ->
|
||||
destination_value = Map.get(updated, relationship.destination_field)
|
||||
|
@ -1299,14 +1280,13 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|
||||
result
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_update(join_action_name, join_params, actor: actor)
|
||||
|> Ash.Changeset.for_update(join_action_name, join_params,
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Changeset.set_context(join_relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.update(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.update(return_notifications?: true)
|
||||
# credo:disable-for-next-line Credo.Check.Refactor.Nesting
|
||||
|> case do
|
||||
{:ok, _updated_join, join_update_notifications} ->
|
||||
|
@ -1452,29 +1432,25 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.for_destroy(
|
||||
join_action_name,
|
||||
%{},
|
||||
actor: actor
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Changeset.set_context(join_relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.destroy(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.destroy(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, join_notifications} ->
|
||||
notifications = join_notifications ++ all_notifications
|
||||
|
||||
record
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor)
|
||||
|> Ash.Changeset.for_destroy(action_name, %{},
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.destroy(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.destroy(return_notifications?: true)
|
||||
# credo:disable-for-next-line Credo.Check.Refactor.Nesting
|
||||
|> case do
|
||||
{:ok, destroy_destination_notifications} ->
|
||||
|
@ -1498,14 +1474,13 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
{:destroy, action_name} ->
|
||||
record
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor)
|
||||
|> Ash.Changeset.for_destroy(action_name, %{},
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(changeset.tenant)
|
||||
|> api.destroy(
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor,
|
||||
return_notifications?: true
|
||||
)
|
||||
|> api.destroy(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, notifications} ->
|
||||
{:cont, {:ok, current_value, notifications ++ all_notifications}}
|
||||
|
@ -1574,14 +1549,10 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
{:ok, result} ->
|
||||
result
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor)
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor, authorize?: opts[:authorize?])
|
||||
|> Ash.Changeset.set_context(join_relationship.context)
|
||||
|> Ash.Changeset.set_tenant(tenant)
|
||||
|> api.destroy(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.destroy(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, notifications} ->
|
||||
{:ok, notifications}
|
||||
|
@ -1613,12 +1584,13 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_update(action_name, %{},
|
||||
relationships: opts[:relationships] || [],
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> maybe_force_change_attribute(relationship, :destination_field, nil)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(tenant)
|
||||
|> api.update(return_notifications?: true, actor: actor, authorize?: opts[:authorize?])
|
||||
|> api.update(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, _unrelated, notifications} ->
|
||||
{:ok, notifications}
|
||||
|
@ -1667,14 +1639,10 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
{:ok, result} ->
|
||||
result
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor)
|
||||
|> Ash.Changeset.for_destroy(action_name, %{}, actor: actor, authorize?: opts[:authorize?])
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(tenant)
|
||||
|> api.destroy(
|
||||
return_notifications?: true,
|
||||
authorize?: opts[:authorize?],
|
||||
actor: actor
|
||||
)
|
||||
|> api.destroy(return_notifications?: true)
|
||||
|> case do
|
||||
{:ok, notifications} ->
|
||||
{:ok, notifications}
|
||||
|
@ -1705,10 +1673,11 @@ defmodule Ash.Actions.ManagedRelationships do
|
|||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_destroy(action_name, %{},
|
||||
relationships: opts[:relationships] || [],
|
||||
actor: actor
|
||||
actor: actor,
|
||||
authorize?: opts[:authorize?]
|
||||
)
|
||||
|> Ash.Changeset.set_context(relationship.context)
|
||||
|> Ash.Changeset.set_tenant(tenant)
|
||||
|> api.destroy(return_notifications?: true, actor: actor, authorize?: opts[:authorize?])
|
||||
|> api.destroy(return_notifications?: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,6 +69,15 @@ defmodule Ash.Actions.Read do
|
|||
| {:error, term}
|
||||
def run(query, action, opts \\ []) do
|
||||
{query, opts} = Ash.Actions.Helpers.add_process_context(query.api, query, opts)
|
||||
|
||||
{query, opts} =
|
||||
if opts[:unsafe_no_authorize?] do
|
||||
{Ash.Query.set_context(query, %{private: %{authorize?: false}}),
|
||||
Keyword.put(opts, :authorize?, false)}
|
||||
else
|
||||
{query, opts}
|
||||
end
|
||||
|
||||
authorize? = authorize?(opts)
|
||||
opts = sanitize_opts(opts, authorize?, query)
|
||||
query = set_tenant_opt(query, opts)
|
||||
|
@ -91,6 +100,7 @@ defmodule Ash.Actions.Read do
|
|||
query,
|
||||
action,
|
||||
actor: engine_opts[:actor],
|
||||
authorize?: engine_opts[:authorize?],
|
||||
timeout: opts[:timeout],
|
||||
tenant: opts[:tenant]
|
||||
)
|
||||
|
@ -209,6 +219,7 @@ defmodule Ash.Actions.Read do
|
|||
Ash.Query.for_read(resource, action.name, input,
|
||||
tenant: tenant,
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
|
||||
|
@ -218,6 +229,7 @@ defmodule Ash.Actions.Read do
|
|||
action,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
end
|
||||
|
|
|
@ -14,15 +14,6 @@ defmodule Ash.Actions.Update do
|
|||
def run(api, changeset, action, opts) do
|
||||
{changeset, opts} = Ash.Actions.Helpers.add_process_context(api, changeset, opts)
|
||||
|
||||
opts =
|
||||
case Map.fetch(changeset.context[:private] || %{}, :actor) do
|
||||
{:ok, actor} ->
|
||||
Keyword.put_new(opts, :actor, actor)
|
||||
|
||||
_ ->
|
||||
opts
|
||||
end
|
||||
|
||||
authorize? = authorize?(opts)
|
||||
return_notifications? = opts[:return_notifications?]
|
||||
actor = opts[:actor]
|
||||
|
@ -178,7 +169,8 @@ defmodule Ash.Actions.Update do
|
|||
api: api,
|
||||
error_path: error_path,
|
||||
changeset:
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor} = context ->
|
||||
Request.resolve(changeset_dependencies, fn %{actor: actor, authorize?: authorize?} =
|
||||
context ->
|
||||
input = changeset_input.(context) || %{}
|
||||
|
||||
tenant =
|
||||
|
@ -209,11 +201,13 @@ defmodule Ash.Actions.Update do
|
|||
|> Ash.Changeset.for_update(action.name, input,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
|> changeset(api, action,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
end
|
||||
|
@ -222,6 +216,7 @@ defmodule Ash.Actions.Update do
|
|||
changeset(changeset, api, action,
|
||||
actor: actor,
|
||||
tenant: tenant,
|
||||
authorize?: authorize?,
|
||||
timeout: timeout
|
||||
)
|
||||
end
|
||||
|
@ -290,7 +285,6 @@ defmodule Ash.Actions.Update do
|
|||
else
|
||||
result =
|
||||
changeset
|
||||
|> Ash.Changeset.put_context(:private, %{actor: actor})
|
||||
|> Ash.Changeset.before_action(
|
||||
&Ash.Actions.ManagedRelationships.setup_managed_belongs_to_relationships(
|
||||
&1,
|
||||
|
|
|
@ -155,16 +155,6 @@ defmodule Ash.Changeset do
|
|||
|
||||
require Ash.Query
|
||||
|
||||
# Used for eager validating identities
|
||||
defmodule ShadowApi do
|
||||
@moduledoc false
|
||||
use Ash.Api
|
||||
|
||||
resources do
|
||||
allow_unregistered? true
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Return a changeset over a resource or a record. `params` can be either attributes, relationship values or arguments.
|
||||
|
||||
|
@ -362,6 +352,11 @@ defmodule Ash.Changeset do
|
|||
doc:
|
||||
"set the actor, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)"
|
||||
],
|
||||
authorize?: [
|
||||
type: :any,
|
||||
doc:
|
||||
"set authorize?, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)"
|
||||
],
|
||||
tenant: [
|
||||
type: :any,
|
||||
doc: "set the tenant on the changeset"
|
||||
|
@ -508,12 +503,13 @@ defmodule Ash.Changeset do
|
|||
changeset
|
||||
|> handle_errors(action.error_handler)
|
||||
|> set_actor(opts)
|
||||
|> set_authorize(opts)
|
||||
|> set_tenant(opts[:tenant] || changeset.tenant)
|
||||
|> Map.put(:__validated_for_action__, action.name)
|
||||
|> Map.put(:action, action)
|
||||
|> cast_params(action, params)
|
||||
|> set_argument_defaults(action)
|
||||
|> run_action_changes(action, opts[:actor])
|
||||
|> run_action_changes(action, opts[:actor], opts[:authorize?])
|
||||
|> add_validations()
|
||||
|> mark_validated(action.name)
|
||||
|> require_arguments(action)
|
||||
|
@ -602,6 +598,7 @@ defmodule Ash.Changeset do
|
|||
changeset
|
||||
|> handle_errors(action.error_handler)
|
||||
|> set_actor(opts)
|
||||
|> set_authorize(opts)
|
||||
|> timeout(changeset.timeout || opts[:timeout])
|
||||
|> set_tenant(opts[:tenant] || changeset.tenant || changeset.data.__metadata__[:tenant])
|
||||
|> Map.put(:action, action)
|
||||
|
@ -610,7 +607,7 @@ defmodule Ash.Changeset do
|
|||
|> set_argument_defaults(action)
|
||||
|> validate_attributes_accepted(action)
|
||||
|> require_values(action.type, false, action.require_attributes)
|
||||
|> run_action_changes(action, opts[:actor])
|
||||
|> run_action_changes(action, opts[:actor], opts[:authorize?])
|
||||
|> set_defaults(changeset.action_type, false)
|
||||
|> add_validations()
|
||||
|> mark_validated(action.name)
|
||||
|
@ -643,13 +640,13 @@ defmodule Ash.Changeset do
|
|||
Enum.reduce(identities, changeset, fn identity, changeset ->
|
||||
changeset =
|
||||
if identity.eager_check_with do
|
||||
validate_identity(changeset, identity)
|
||||
validate_identity(changeset, identity, identity.eager_check_with)
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
||||
if identity.pre_check_with do
|
||||
before_action(changeset, &validate_identity(&1, identity))
|
||||
before_action(changeset, &validate_identity(&1, identity, identity.pre_check_with))
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
@ -659,37 +656,41 @@ defmodule Ash.Changeset do
|
|||
|
||||
defp validate_identity(
|
||||
%{context: %{private: %{upsert?: true, upsert_identity: name}}} = changeset,
|
||||
%{name: name}
|
||||
%{name: name},
|
||||
_api
|
||||
) do
|
||||
changeset
|
||||
end
|
||||
|
||||
defp validate_identity(
|
||||
%{action: %{soft?: true}} = changeset,
|
||||
identity
|
||||
identity,
|
||||
api
|
||||
) do
|
||||
do_validate_identity(changeset, identity)
|
||||
do_validate_identity(changeset, identity, api)
|
||||
end
|
||||
|
||||
defp validate_identity(
|
||||
%{action: %{type: type}} = changeset,
|
||||
identity
|
||||
identity,
|
||||
api
|
||||
)
|
||||
when type in [:create, :update] do
|
||||
do_validate_identity(changeset, identity)
|
||||
do_validate_identity(changeset, identity, api)
|
||||
end
|
||||
|
||||
defp validate_identity(
|
||||
%{action: %{type: type}} = changeset,
|
||||
identity
|
||||
identity,
|
||||
api
|
||||
)
|
||||
when type in [:create, :update] do
|
||||
do_validate_identity(changeset, identity)
|
||||
do_validate_identity(changeset, identity, api)
|
||||
end
|
||||
|
||||
defp validate_identity(changeset, _), do: changeset
|
||||
defp validate_identity(changeset, _, _), do: changeset
|
||||
|
||||
defp do_validate_identity(changeset, identity) do
|
||||
defp do_validate_identity(changeset, identity, api) do
|
||||
if Enum.any?(identity.keys, &changing_attribute?(changeset, &1)) do
|
||||
action = Ash.Resource.Info.primary_action(changeset.resource, :read).name
|
||||
|
||||
|
@ -699,10 +700,14 @@ defmodule Ash.Changeset do
|
|||
end)
|
||||
|
||||
changeset.resource
|
||||
|> Ash.Query.for_read(action, %{}, tenant: changeset.tenant)
|
||||
|> Ash.Query.for_read(action, %{},
|
||||
tenant: changeset.tenant,
|
||||
actor: changeset.context[:private][:actor],
|
||||
authorize?: changeset.context[:private][:authorize?]
|
||||
)
|
||||
|> Ash.Query.do_filter(values)
|
||||
|> Ash.Query.limit(1)
|
||||
|> ShadowApi.read_one()
|
||||
|> api.read_one()
|
||||
|> case do
|
||||
{:ok, nil} ->
|
||||
changeset
|
||||
|
@ -774,6 +779,14 @@ defmodule Ash.Changeset do
|
|||
end
|
||||
end
|
||||
|
||||
defp set_authorize(changeset, opts) do
|
||||
if Keyword.has_key?(opts, :authorize?) do
|
||||
put_context(changeset, :private, %{authorize?: opts[:authorize?]})
|
||||
else
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
defp raise_no_action(resource, action, type) do
|
||||
available_actions =
|
||||
resource
|
||||
|
@ -860,7 +873,7 @@ defmodule Ash.Changeset do
|
|||
end)
|
||||
end
|
||||
|
||||
defp run_action_changes(changeset, %{changes: changes}, actor) do
|
||||
defp run_action_changes(changeset, %{changes: changes}, actor, authorize?) do
|
||||
changes = changes ++ Ash.Resource.Info.changes(changeset.resource, changeset.action_type)
|
||||
|
||||
Enum.reduce(changes, changeset, fn
|
||||
|
@ -872,7 +885,7 @@ defmodule Ash.Changeset do
|
|||
module.validate(changeset, opts) == :ok
|
||||
end) do
|
||||
{:ok, opts} = module.init(opts)
|
||||
module.change(changeset, opts, %{actor: actor})
|
||||
module.change(changeset, opts, %{actor: actor, authorize?: authorize?})
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
@ -2039,6 +2052,7 @@ defmodule Ash.Changeset do
|
|||
relationship.destination
|
||||
|> Ash.Query.for_read(action, %{},
|
||||
actor: changeset.context[:private][:actor],
|
||||
authorize?: changeset.context[:private][:authorize?],
|
||||
tenant: changeset.tenant
|
||||
)
|
||||
|> Ash.Query.limit(Enum.count(input))
|
||||
|
|
|
@ -191,7 +191,7 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Query.for_read(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|> Ash.Query.filter(filters)
|
||||
else
|
||||
|
@ -200,13 +200,15 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Query.for_read(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
|
||||
if unquote(interface.get? || action.get?) do
|
||||
query
|
||||
|> unquote(api).read_one(Keyword.drop(opts, [:query, :tenant]))
|
||||
|> unquote(api).read_one(
|
||||
Keyword.drop(opts, [:query, :tenant, :authorize?, :actor])
|
||||
)
|
||||
|> case do
|
||||
{:ok, nil} ->
|
||||
{:error, Ash.Error.Query.NotFound.exception(resource: query.resource)}
|
||||
|
@ -218,7 +220,10 @@ defmodule Ash.CodeInterface do
|
|||
{:error, error}
|
||||
end
|
||||
else
|
||||
unquote(api).read(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
unquote(api).read(
|
||||
query,
|
||||
Keyword.drop(opts, [:query, :tenant, :actor, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -259,7 +264,7 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Query.for_read(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|> Ash.Query.filter(filters)
|
||||
else
|
||||
|
@ -268,13 +273,15 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Query.for_read(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
|
||||
if unquote(interface.get? || action.get?) do
|
||||
query
|
||||
|> unquote(api).read_one!(Keyword.drop(opts, [:query, :tenant]))
|
||||
|> unquote(api).read_one!(
|
||||
Keyword.drop(opts, [:query, :tenant, :authorize?, :actor])
|
||||
)
|
||||
|> case do
|
||||
nil ->
|
||||
raise Ash.Error.Query.NotFound, resource: query.resource
|
||||
|
@ -283,7 +290,10 @@ defmodule Ash.CodeInterface do
|
|||
result
|
||||
end
|
||||
else
|
||||
unquote(api).read!(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
unquote(api).read!(
|
||||
query,
|
||||
Keyword.drop(opts, [:query, :tenant, :actor, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -316,10 +326,13 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_create(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).create(changeset, Keyword.drop(opts, [:actor, :changeset, :tenant]))
|
||||
unquote(api).create(
|
||||
changeset,
|
||||
Keyword.drop(opts, [:actor, :changeset, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -350,10 +363,13 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_create(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).create!(changeset, Keyword.drop(opts, [:actor, :changeset]))
|
||||
unquote(api).create!(
|
||||
changeset,
|
||||
Keyword.drop(opts, [:actor, :changeset, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -386,10 +402,10 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_update(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).update(changeset, Keyword.drop(opts, [:actor, :tenant]))
|
||||
unquote(api).update(changeset, Keyword.drop(opts, [:actor, :tenant, :authorize?]))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -422,10 +438,13 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_update(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).update!(changeset, Keyword.drop(opts, [:actor, :tenant]))
|
||||
unquote(api).update!(
|
||||
changeset,
|
||||
Keyword.drop(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -458,10 +477,13 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_destroy(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).destroy(changeset, Keyword.drop(opts, [:actor, :tenant]))
|
||||
unquote(api).destroy(
|
||||
changeset,
|
||||
Keyword.drop(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -494,10 +516,13 @@ defmodule Ash.CodeInterface do
|
|||
|> Ash.Changeset.for_destroy(
|
||||
unquote(action.name),
|
||||
input,
|
||||
Keyword.take(opts, [:actor, :tenant])
|
||||
Keyword.take(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
|
||||
unquote(api).destroy!(changeset, Keyword.drop(opts, [:actor, :tenant]))
|
||||
unquote(api).destroy!(
|
||||
changeset,
|
||||
Keyword.drop(opts, [:actor, :tenant, :authorize?])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -793,7 +793,7 @@ defmodule Ash.Engine.Request do
|
|||
|
||||
new_query
|
||||
|> Map.put(:api, request.api)
|
||||
|> Ash.Actions.Read.unpaginated_read()
|
||||
|> Ash.Actions.Read.unpaginated_read(actor: request.actor, unsafe_no_authorize?: false)
|
||||
|> case do
|
||||
{:ok, results} ->
|
||||
pkey = Ash.Resource.Info.primary_key(request.resource)
|
||||
|
|
|
@ -171,6 +171,16 @@ defmodule Ash.Filter do
|
|||
end
|
||||
end
|
||||
|
||||
# Used for fetching related data in filters, which will have already had authorization rules applied
|
||||
defmodule ShadowApi do
|
||||
@moduledoc false
|
||||
use Ash.Api
|
||||
|
||||
resources do
|
||||
allow_unregistered? true
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses a filter statement, accepting only public attributes/relationships
|
||||
|
||||
|
@ -1320,7 +1330,7 @@ defmodule Ash.Filter do
|
|||
}
|
||||
|
||||
relationship.destination
|
||||
|> Ash.Query.new(api)
|
||||
|> Ash.Query.new(ShadowApi)
|
||||
|> Ash.Query.do_filter(filter)
|
||||
|> Ash.Actions.Read.unpaginated_read()
|
||||
|> case do
|
||||
|
|
|
@ -228,6 +228,11 @@ defmodule Ash.Query do
|
|||
doc:
|
||||
"set the actor, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)"
|
||||
],
|
||||
authorize?: [
|
||||
type: :boolean,
|
||||
doc:
|
||||
"set authorize?, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)"
|
||||
],
|
||||
tenant: [
|
||||
type: :any,
|
||||
doc: "set the tenant on the query"
|
||||
|
@ -264,11 +269,12 @@ defmodule Ash.Query do
|
|||
query
|
||||
|> timeout(query.timeout || opts[:timeout])
|
||||
|> set_actor(opts)
|
||||
|> set_authorize?(opts)
|
||||
|> Ash.Query.set_tenant(opts[:tenant] || query.tenant)
|
||||
|> Map.put(:action, action)
|
||||
|> Map.put(:__validated_for_action__, action_name)
|
||||
|> cast_params(action, args)
|
||||
|> run_preparations(action, opts[:actor])
|
||||
|> run_preparations(action, opts[:actor], opts[:authorize?])
|
||||
|> add_action_filters(action, opts[:actor])
|
||||
|> require_arguments(action)
|
||||
else
|
||||
|
@ -294,6 +300,14 @@ defmodule Ash.Query do
|
|||
end
|
||||
end
|
||||
|
||||
defp set_authorize?(query, opts) do
|
||||
if Keyword.has_key?(opts, :authorize?) do
|
||||
put_context(query, :private, %{authorize?: opts[:authorize?]})
|
||||
else
|
||||
query
|
||||
end
|
||||
end
|
||||
|
||||
defp require_arguments(query, action) do
|
||||
query
|
||||
|> set_argument_defaults(action)
|
||||
|
@ -359,14 +373,14 @@ defmodule Ash.Query do
|
|||
Enum.any?(action.arguments, &(&1.private? == false && to_string(&1.name) == name))
|
||||
end
|
||||
|
||||
defp run_preparations(query, action, actor) do
|
||||
defp run_preparations(query, action, actor, authorize?) do
|
||||
query.resource
|
||||
|> Ash.Resource.Info.preparations()
|
||||
|> Enum.concat(action.preparations || [])
|
||||
|> Enum.reduce(query, fn %{preparation: {module, opts}}, query ->
|
||||
case module.init(opts) do
|
||||
{:ok, opts} ->
|
||||
case module.prepare(query, opts, %{actor: actor}) do
|
||||
case module.prepare(query, opts, %{actor: actor, authorize?: authorize?}) do
|
||||
%__MODULE__{} = prepared ->
|
||||
prepared
|
||||
|
||||
|
|
|
@ -125,8 +125,8 @@ defmodule Ash.Test.Changeset.AuthorizerTest do
|
|||
# Filter always fails on creates
|
||||
assert_raise Ash.Error.Forbidden, fn ->
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "test"})
|
||||
|> Api.create!(authorize?: true)
|
||||
|> Ash.Changeset.for_create(:create, %{title: "test"}, authorize?: true)
|
||||
|> Api.create!()
|
||||
end
|
||||
|
||||
good_post =
|
||||
|
@ -142,13 +142,13 @@ defmodule Ash.Test.Changeset.AuthorizerTest do
|
|||
# Filters apply to the base data
|
||||
assert_raise Ash.Error.Forbidden, fn ->
|
||||
bad_post
|
||||
|> Ash.Changeset.for_update(:update, %{title: "next"})
|
||||
|> Api.update!(authorize?: true)
|
||||
|> Ash.Changeset.for_update(:update, %{title: "next"}, authorize?: true)
|
||||
|> Api.update!()
|
||||
end
|
||||
|
||||
good_post
|
||||
|> Ash.Changeset.for_update(:update, %{title: "next"})
|
||||
|> Api.update!(authorize?: true)
|
||||
|> Ash.Changeset.for_update(:update, %{title: "next"}, authorize?: true)
|
||||
|> Api.update!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue