Skip to content

Commit

Permalink
Merge pull request #36 from jotaviobiondo/support_functions_in_query_…
Browse files Browse the repository at this point in the history
…scope_authorization_args

Support functions in query scope authorization args
  • Loading branch information
gabrielpra1 authored Jun 16, 2021
2 parents d678d50 + ea87479 commit 08c6c22
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 11 deletions.
3 changes: 3 additions & 0 deletions lib/middlewares/query_scope_authorization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ defmodule Rajska.QueryScopeAuthorization do
- `User`: a module that will be passed to `c:Rajska.Authorization.has_user_access?/3`. It must define a struct.
* `:args`
- `%{user_id: [:params, :id]}`: where `user_id` is the scoped field and `id` is an argument nested inside the `params` argument.
This form also accepts a function in the array (the same way as described in the [Kernel.get_in/2](https://hexdocs.pm/elixir/Kernel.html#get_in/2-functions-as-keys)).
This way, we can also use `%{user_id: [:params, Access.all(), :id]}` and for an input arg like `params: [%{id: 1}, %{id: 2}]`,
the builded struct will have the value `%User{user_id: [1, 2]}`.
- `:id`: this is the same as `%{id: :id}`, where `:id` is both the query argument and the scoped field that will be passed to `c:Rajska.Authorization.has_user_access?/3`
- `[:code, :user_group_id]`: this is the same as `%{code: :code, user_group_id: :user_group_id}`, where `code` and `user_group_id` are both query arguments and scoped fields.
* `:optional` (optional) - when set to true the arguments are optional, so if no argument is provided, the query will be authorized. Defaults to false.
Expand Down
11 changes: 8 additions & 3 deletions lib/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,13 @@ defmodule Rajska.Schema do
defp validate_args!(args) when is_map(args) do
Enum.each(args, fn
{field, value} when is_atom(field) and is_atom(value) -> :ok
{field, values} when is_atom(field) and is_list(values) -> validate_list_of_atoms!(values)
{field, values} when is_atom(field) and is_list(values) -> validate_list_of_atoms_or_function!(values)
field_value -> raise "the following args option is invalid: #{inspect(field_value)}. Since the provided args is a map, you should provide an atom key and an atom or list of atoms value."
end)
end

defp validate_args!(args) when is_list(args), do: validate_list_of_atoms!(args)

defp validate_args!(args) when is_atom(args), do: :ok

defp validate_args!(args), do: raise "the following args option is invalid: #{inspect(args)}"

defp validate_list_of_atoms!(args) do
Expand All @@ -145,4 +143,11 @@ defmodule Rajska.Schema do
arg -> raise "the following args option is invalid: #{inspect(args)}. Expected a list of atoms, but found #{inspect(arg)}"
end)
end

defp validate_list_of_atoms_or_function!(args) do
Enum.each(args, fn
arg when is_atom(arg) or is_function(arg) -> :ok
arg -> raise "the following args option is invalid: #{inspect(args)}. Expected a list of atoms or functions, but found #{inspect(arg)}"
end)
end
end
56 changes: 48 additions & 8 deletions test/middlewares/schema_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,41 @@ defmodule Rajska.SchemaTest do
)
end

test "Raises if args option is invalid" do
assert_raise(
RuntimeError,
~r/Query get_user is configured incorrectly, the following args option is invalid: "args"/,
fn ->
defmodule SchemaInvalidArgs do
@args_option_test_cases [
{false, :arg},
{false, [:arg1, :arg2]},
{false, %{arg: :arg}},
{false, %{arg: [:arg]}},
# This is not the correct usage of &Access.all/0 that is going to be used in the Kernel.get_in/2 in
# Rajska.QueryScopeAuthorization.get_scope_field_value/2, but this is necessary because it's not possible to use
# Access.all() (the correct usage). But for testing purpose only this is fine.
{false, %{arg: [&Access.all/0, :arg]}},
{true, "args"},
{true, 1},
{true, ["args"]},
{true, [1]},
{true, %{arg: ["arg"]}},
{true, %{arg: [1]}},
{true, [&Access.all/0, :arg]},
]

for {{should_raise, args_value}, index} <- Enum.with_index(@args_option_test_cases) do
@should_raise should_raise
@args_value args_value
@index index
@base_message "if args option is #{inspect(args_value)}"
@message if should_raise, do: "Raises #{@base_message}", else: "Not raises #{@base_message}"

test @message do
args_value = @args_value
index = @index

define_query_fn = fn ->
defmodule String.to_atom("SchemaInvalidArgs#{index}") do
use Absinthe.Schema

@args_value args_value

def context(ctx), do: Map.put(ctx, :authorization, Authorization)

def middleware(middleware, field, %{identifier: identifier})
Expand All @@ -164,13 +191,26 @@ defmodule Rajska.SchemaTest do

query do
field :get_user, :string do
middleware Rajska.QueryAuthorization, [permit: :user, scope: User, args: "args"]
middleware Rajska.QueryAuthorization, [permit: :user, scope: User, args: @args_value]
resolve fn _args, _info -> {:ok, "bob"} end
end
end
end
end
)

if @should_raise do
invalid_value = if is_map(@args_value), do: @args_value[:arg], else: @args_value
escaped_invalid_value = invalid_value |> inspect() |> Regex.escape()

assert_raise(
RuntimeError,
~r/Query get_user is configured incorrectly, the following args option is invalid: #{escaped_invalid_value}/,
define_query_fn
)
else
define_query_fn.()
end
end
end

test "Raises if optional option is not a boolean" do
Expand Down

0 comments on commit 08c6c22

Please sign in to comment.