diff --git a/lib/jsonapi/plugs/query_parser.ex b/lib/jsonapi/plugs/query_parser.ex index d0999386..d849c2ad 100644 --- a/lib/jsonapi/plugs/query_parser.ex +++ b/lib/jsonapi/plugs/query_parser.ex @@ -27,9 +27,14 @@ defmodule JSONAPI.QueryParser do plug JSONAPI.QueryParser, filter: ~w(title), sort: ~w(created_at title), + include: ~w(others) # optionally specify a list of allowed includes. view: MyView ``` + If you specify which includes are allowed, any include name not in the list + will produce an error. If you omit the `include` list then all relationships + specified by the given resource will be allowed. + If your controller's index function receives a query with params inside those bounds it will build a `JSONAPI.Config` that has all the validated and parsed fields for your usage. The final configuration will be added to assigns @@ -206,6 +211,8 @@ defmodule JSONAPI.QueryParser do |> Enum.map(&underscore/1) Enum.reduce(includes, [], fn inc, acc -> + check_include_validity!(inc, config) + if inc =~ ~r/\w+\.\w+/ do acc ++ handle_nested_include(inc, valid_includes, config) else @@ -225,6 +232,22 @@ defmodule JSONAPI.QueryParser do end) end + defp check_include_validity!(key, %Config{opts: opts, view: view}) do + if opts do + check_include_validity!(key, Keyword.get(opts, :include), view) + end + end + + defp check_include_validity!(key, allowed_includes, view) when is_list(allowed_includes) do + unless key in allowed_includes do + raise_invalid_include_query(key, view.type()) + end + end + + defp check_include_validity!(_key, nil, _view) do + # all includes are allowed if none are specified in input config + end + @spec handle_nested_include(key :: String.t(), valid_include :: list(), config :: Config.t()) :: list() | no_return() def handle_nested_include(key, valid_include, config) do diff --git a/test/jsonapi/plugs/query_parser_test.exs b/test/jsonapi/plugs/query_parser_test.exs index 5f5976a0..84cccdf1 100644 --- a/test/jsonapi/plugs/query_parser_test.exs +++ b/test/jsonapi/plugs/query_parser_test.exs @@ -108,6 +108,18 @@ defmodule JSONAPI.QueryParserTest do end end + test "parse_include/2 errors with limited allowed includes" do + config = struct(Config, view: MyView, opts: [include: ~w(author comments comments.user)]) + + assert_raise InvalidQuery, "invalid include, best_friends for type mytype", fn -> + parse_include(config, "best_friends,author") + end + + assert parse_include(config, "author,comments").include == [:author, :comments] + + assert parse_include(config, "author,comments.user").include == [:author, {:comments, :user}] + end + test "parse_fields/2 turns a fields map into a map of validated fields" do config = struct(Config, view: MyView) assert parse_fields(config, %{"mytype" => "id,text"}).fields == %{"mytype" => [:id, :text]}