This commit is contained in:
Zach Daniel 2019-11-26 01:50:53 -05:00
parent afdd294e7f
commit ef2f828250
No known key found for this signature in database
GPG key ID: A57053A671EE649E
6 changed files with 33 additions and 19 deletions

View file

@ -19,3 +19,4 @@
* all actions need to be performed in a transaction
* document authorization thoroughly. *batch* (default) checks need to return a list of `ids` for which the check passed.
* So many parts of the system are reliant on things having an `id` key explicitly. THis will need to be addressed some day, and will be a huge pain in the ass
* Validate that the user resource has a get action

View file

@ -58,14 +58,14 @@ defmodule Ash do
resource.data_layer()
end
def get(resource, id, params \\ %{}, action \\ nil) do
def get(resource, id, params \\ %{}, action \\ %{}) do
# TODO: Figure out this interface
params_with_filter =
params
|> Map.put_new(:filter, %{})
|> Map.update!(:filter, &Map.put(&1, :id, id))
case read(resource, params_with_filter, action) do
case read(resource, params_with_filter) do
{:ok, %{results: [single_result]}} ->
{:ok, single_result}
@ -81,26 +81,27 @@ defmodule Ash do
end
# TODO: params
def read(resource, user, params \\ %{}, action \\ nil) do
action = action || primary_action(resource, :read)
def read(resource, params \\ %{}) do
action = Map.get(params, :action) || primary_action(resource, :read)
params = Map.put_new(params, :user, :__none__)
Ash.DataLayer.Actions.run_read_action(resource, action, params)
end
# TODO: auth
def create(resource, attributes, relationships, params \\ %{}, action \\ nil) do
action = action || primary_action(resource, :create)
def create(resource, attributes, relationships, params \\ %{}) do
action = Map.get(params, :action) || primary_action(resource, :create)
Ash.DataLayer.Actions.run_create_action(resource, action, attributes, relationships, params)
end
# TODO: auth
def update(%resource{} = record, attributes, relationships, params \\ %{}, action \\ nil) do
action = action || primary_action(resource, :update)
def update(%resource{} = record, attributes, relationships, params \\ %{}) do
action = Map.get(params, :action) || primary_action(resource, :update)
Ash.DataLayer.Actions.run_update_action(record, action, attributes, relationships, params)
end
# TODO: auth
def destroy(%resource{} = record, params \\ %{}, action \\ nil) do
action = action || primary_action(resource, :destroy)
def destroy(%resource{} = record, params \\ %{}) do
action = Map.get(params, :action) || primary_action(resource, :destroy)
Ash.DataLayer.Actions.run_destroy_action(record, action, params)
end

View file

@ -1,6 +1,9 @@
defmodule Ash.Authorization.Authorizer do
alias Ash.Authorization.Rule
def authorize_precheck(:__none__, rules, _context),
do: {%{prediction: :allow}, Enum.map(rules, fn _ -> %{} end)}
def authorize_precheck(user, rules, context) do
rules
|> Enum.reduce({%{}, []}, fn rule, {instructions, per_check_data} ->
@ -17,6 +20,8 @@ defmodule Ash.Authorization.Authorizer do
# Never call authorize w/o first calling authorize_precheck before
# the operation
def authorize(:__none__, _, _, _, _), do: :allow
def authorize(user, data, rules, context, per_check_data) do
{_decision, remaining_records} =
rules

View file

@ -9,6 +9,7 @@ defmodule Ash.Authorization.BuiltIn do
item
|> Map.get(relationship_name)
|> Kernel.||([])
|> List.wrap()
|> Enum.find(fn related ->
Map.get(related, relationship.destination_field) == user.id
end)

View file

@ -51,7 +51,7 @@ defmodule Ash.Authorization.Rule do
end
def run_check(
%{check: check, extra_context: extra_context, kind: kind},
%{check: check, extra_context: extra_context, kind: kind} = rule,
user,
data,
context

View file

@ -2,7 +2,7 @@ defmodule Ash.DataLayer.Actions do
def run_create_action(resource, action, attributes, relationships, params) do
case Ash.Data.create(resource, action, attributes, relationships, params) do
{:ok, record} ->
Ash.Data.side_load(record, Map.get(params, :include, []), resource)
Ash.Data.side_load(record, Map.get(params, :side_load, []), resource)
{:error, error} ->
{:error, error}
@ -11,7 +11,8 @@ defmodule Ash.DataLayer.Actions do
def run_update_action(%resource{} = record, action, attributes, relationships, params) do
with {:ok, record} <- Ash.Data.update(record, action, attributes, relationships, params),
{:ok, [record]} <- Ash.Data.side_load([record], Map.get(params, :include, []), resource) do
{:ok, [record]} <-
Ash.Data.side_load([record], Map.get(params, :side_load, []), resource) do
{:ok, record}
else
{:error, error} -> {:error, error}
@ -29,27 +30,32 @@ defmodule Ash.DataLayer.Actions do
params: params
}
user = Map.get(params, :user)
user = Map.get(params || %{}, :user)
with {%{prediction: prediction} = instructions, per_check_data}
when prediction != :unauthorized <-
Ash.Authorization.Authorizer.authorize_precheck(user, action.rules, auth_context),
params <- add_auth_side_loads(params, instructions),
{:ok, query} <- Ash.Data.resource_to_query(resource),
{:ok, filtered_query} <- Ash.Data.filter(resource, query, params),
{:ok, paginator} <-
Ash.DataLayer.Paginator.paginate(resource, action, filtered_query, params),
{:ok, found} <- Ash.Data.get_many(paginator.query, resource),
side_load_param <-
deep_merge_side_loads(
Map.get(params, :side_load, []),
Map.get(instructions, :side_load, [])
),
{:ok, side_loaded} <-
Ash.Data.side_load(found, side_load_param, resource),
:allow <-
Ash.Authorization.Authorizer.authorize(
user,
found,
side_loaded,
action.rules,
auth_context,
per_check_data
),
{:ok, result} <- Ash.Data.side_load(found, Map.get(params, :include, []), resource) do
{:ok, %{paginator | results: result}}
) do
{:ok, %{paginator | results: side_loaded}}
else
{%{prediction: :unauthorized}, _} ->
# TODO: Nice errors here!