From 18c91b68700a80ad14a7b24cde5e8228e3dedaf8 Mon Sep 17 00:00:00 2001 From: Jeroen Visser Date: Thu, 16 Jul 2020 00:37:36 +0200 Subject: [PATCH] Don't require QueryAuthorization to be first This would break usage in Relay. This can be improved to check if any Resolution middleware, and if those are found, compilation could still be halted like before. This ensures that there are no accidental 'open doors' when middleware isn't added before QueryAuthorization. --- lib/schema.ex | 101 +++++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/lib/schema.ex b/lib/schema.ex index f2156fd..fcff15b 100644 --- a/lib/schema.ex +++ b/lib/schema.ex @@ -14,53 +14,56 @@ defmodule Rajska.Schema do } @spec add_query_authorization( - [Middleware.spec(), ...], - Field.t(), - module() - ) :: [Middleware.spec(), ...] + [Middleware.spec(), ...], + Field.t(), + module() + ) :: [Middleware.spec(), ...] def add_query_authorization(middleware, %{definition: Introspection}, _authorizaton), do: middleware - def add_query_authorization( - [{{QueryAuthorization, :call}, config} = query_authorization | middleware] = _middleware, - %Field{name: query_name}, - authorization - ) do - validate_query_auth_config!(config, authorization, query_name) - - [query_authorization | middleware] - end - def add_query_authorization(_middleware, %Field{name: name}, _authorization) do - raise "No permission specified for query #{name}" + def add_query_authorization(middleware, %Field{name: query_name}, authorization) do + middleware + |> Enum.find(&match?({{QueryAuthorization, :call}, _config}, &1)) + |> case do + {{QueryAuthorization, :call}, config} -> + validate_query_auth_config!(config, authorization, query_name) + + nil -> + raise "No permission specified for query #{query_name}" + end + + middleware end @spec add_object_authorization([Middleware.spec(), ...]) :: [Middleware.spec(), ...] - def add_object_authorization([{{QueryAuthorization, :call}, _} = query_authorization | middleware]) do + def add_object_authorization([ + {{QueryAuthorization, :call}, _} = query_authorization | middleware + ]) do [query_authorization, ObjectAuthorization] ++ middleware end def add_object_authorization(middleware), do: [ObjectAuthorization | middleware] @spec add_field_authorization( - [Middleware.spec(), ...], - Field.t(), - Object.t() - ) :: [Middleware.spec(), ...] + [Middleware.spec(), ...], + Field.t(), + Object.t() + ) :: [Middleware.spec(), ...] def add_field_authorization(middleware, %Field{identifier: field}, object) do [{{FieldAuthorization, :call}, object: object, field: field} | middleware] end @spec validate_query_auth_config!( - [ - permit: atom(), - scope: false | module(), - args: %{} | [] | atom(), - optional: false | true, - rule: atom() - ], - module(), - String.t() - ) :: :ok | Exception.t() + [ + permit: atom(), + scope: false | module(), + args: %{} | [] | atom(), + optional: false | true, + rule: atom() + ], + module(), + String.t() + ) :: :ok | Exception.t() def validate_query_auth_config!(config, authorization, query_name) do permit = Keyword.get(config, :permit) @@ -77,22 +80,25 @@ defmodule Rajska.Schema do validate_scope!(scope, permit, authorization) validate_args!(args) rescue - e in RuntimeError -> reraise "Query #{query_name} is configured incorrectly, #{e.message}", __STACKTRACE__ + e in RuntimeError -> + reraise "Query #{query_name} is configured incorrectly, #{e.message}", __STACKTRACE__ end end - defp validate_presence!(nil, option), do: raise "#{inspect(option)} option must be present." + defp validate_presence!(nil, option), do: raise("#{inspect(option)} option must be present.") defp validate_presence!(_value, _option), do: :ok defp validate_boolean!(value, _option) when is_boolean(value), do: :ok - defp validate_boolean!(_value, option), do: raise "#{inspect(option)} option must be a boolean." + + defp validate_boolean!(_value, option), + do: raise("#{inspect(option)} option must be a boolean.") defp validate_atom!(value, _option) when is_atom(value), do: :ok - defp validate_atom!(_value, option), do: raise "#{inspect(option)} option must be an atom." + defp validate_atom!(_value, option), do: raise("#{inspect(option)} option must be an atom.") defp validate_scope!(nil, role, authorization) do unless Enum.member?(authorization.not_scoped_roles(), role), - do: raise ":scope option must be present for role #{inspect(role)}." + do: raise(":scope option must be present for role #{inspect(role)}.") end defp validate_scope!(false, _role, _authorization), do: :ok @@ -100,14 +106,20 @@ defmodule Rajska.Schema do defp validate_scope!(scope, _role, _authorization) when is_atom(scope) do struct!(scope) rescue - UndefinedFunctionError -> reraise ":scope option #{inspect(scope)} is not a struct.", __STACKTRACE__ + UndefinedFunctionError -> + reraise ":scope option #{inspect(scope)} is not a struct.", __STACKTRACE__ end 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_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." + {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_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 @@ -115,12 +127,17 @@ defmodule Rajska.Schema do 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_args!(args), do: raise("the following args option is invalid: #{inspect(args)}") defp validate_list_of_atoms!(args) do Enum.each(args, fn - arg when is_atom(arg) -> :ok - arg -> raise "the following args option is invalid: #{inspect(args)}. Expected a list of atoms, but found #{inspect(arg)}" + arg when is_atom(arg) -> + :ok + + arg -> + raise "the following args option is invalid: #{inspect(args)}. Expected a list of atoms, but found #{ + inspect(arg) + }" end) end end