Skip to content

Commit

Permalink
Disabling the categories in graphql AST
Browse files Browse the repository at this point in the history
  • Loading branch information
Janusz Jakubiec authored and Janusz Jakubiec committed Nov 3, 2022
1 parent 12e2091 commit bf8983e
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 42 deletions.
29 changes: 23 additions & 6 deletions big_tests/tests/graphql_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ common_tests() ->
categories_disabled_tests() ->
[category_disabled_error_test,
admin_checks_auth,
category_does_not_exists_error].
category_does_not_exists_error,
listener_reply_with_validation_error,
multiple_categories_query_test].

init_per_suite(Config) ->
Config1 = escalus:init_per_suite(Config),
Expand Down Expand Up @@ -158,11 +160,10 @@ auth_domain_admin_checks_auth(Config) ->
?assertAdminAuth(Domain, 'DOMAIN_ADMIN', 'AUTHORIZED', Res).

category_disabled_error_test(Config) ->
Ep = ?config(schema_endpoint, Config),
Status = execute(Ep, admin_server_get_loglevel_body(), undefined),
get_bad_request(Status),
Status = execute_auth(admin_server_get_loglevel_body(), Config),
{_Code, #{<<"errors">> := [Msg]}} = Status,
?assertEqual(<<"category_disabled">>, get_value([extensions, code], Msg)).
?assertEqual(<<"category_disabled">>, get_value([extensions, code], Msg)),
?assertEqual([<<"server">>], get_value([path], Msg)).

category_does_not_exists_error(Config) ->
Ep = ?config(schema_endpoint, Config),
Expand All @@ -171,6 +172,19 @@ category_does_not_exists_error(Config) ->
{_Code, #{<<"errors">> := [Msg]}} = Status,
?assertEqual(<<"parser_error">>, get_value([extensions, code], Msg)).

listener_reply_with_validation_error(Config) ->
Ep = ?config(schema_endpoint, Config),
Body = #{<<"query">> => <<"query Q1 { field } query Q1 { field }">>,
<<"operationName">> => <<"Q1">>},
{Status, Data} = execute(Ep, Body, undefined).

multiple_categories_query_test(Config) ->
Status = execute_auth(user_check_auth_multiple(), Config),
{_Code, #{<<"errors">> := [ErrorMsg], <<"data">> := DataMsg}} = Status,
?assertEqual(<<"category_disabled">>, get_value([extensions, code], ErrorMsg)),
?assertEqual([<<"server">>], get_value([path], ErrorMsg)),
?assertEqual(<<"AUTHORIZED">>, get_value([checkAuth, authStatus], DataMsg)).

%% Helpers

assert_auth(Auth, {Status, Data}) ->
Expand All @@ -194,7 +208,10 @@ admin_check_auth_body() ->
#{query => "{ checkAuth { domain authType authStatus } }"}.

admin_server_get_loglevel_body() ->
#{query => "{ server { getLogLevel } }"}.
#{query => "{ server { getLoglevel } }"}.

user_check_auth_body() ->
#{query => "{ checkAuth { username authStatus } }"}.

user_check_auth_multiple() ->
#{query => "{ checkAuth { authStatus } server { getLoglevel } }"}.
3 changes: 3 additions & 0 deletions src/graphql/directive/mongoose_graphql_directive_use.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ handle_directive(#directive{id = <<"use">>, args = Args}, #schema_field{} = Fiel
Field;
{_, _} ->
Fun = resolve_not_loaded_fun(UnloadedModules, UnloadedServices),
io:format("tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt\n\n\n"),
io:format("~p\n", [Field#schema_field{resolve = Fun}]),
io:format("tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt\n\n\n"),
Field#schema_field{resolve = Fun}
end;
{error, not_found} ->
Expand Down
4 changes: 3 additions & 1 deletion src/graphql/mongoose_graphql.erl
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ execute(Ep, #{document := Doc,
authorized => AuthStatus,
error_module => mongoose_graphql_errors},
Ast3 = mongoose_graphql_directive:process_directives(Ctx2, Ast2),
{ok, graphql:execute(Ep, Ctx2, Ast3)}
AllowedCategories = maps:get(allowed_categories, Ctx2, []),
Ast4 = mongoose_graphql_check_categories:process_ast(Ast3, AllowedCategories),
{ok, graphql:execute(Ep, Ctx2, Ast4)}
catch
throw:{error, Err} ->
{error, Err};
Expand Down
50 changes: 50 additions & 0 deletions src/graphql/mongoose_graphql_check_categories.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-module(mongoose_graphql_check_categories).

-export([process_ast/2]).

-include_lib("graphql/src/graphql_schema.hrl").
-include_lib("graphql/src/graphql_internal.hrl").
-include_lib("graphql/include/graphql.hrl").
-include_lib("jid/include/jid.hrl").

-type document() :: #document{}.
-type categories() :: [binary()].

-include("mongoose.hrl").

-spec process_ast(document(), categories()) -> document().
process_ast(#document{definitions = Definitions} = Document, Categories) ->
case Categories of
[] ->
Document;
_ ->
Definitions2 = lists:map(fun(#op{schema = Schema} = Op) ->
parse_schema(Schema, Op, Categories)
end, Definitions),
#document{definitions = Definitions2}
end.

parse_schema(#object_type{fields = Fields} = Schema, Op, Categories) ->
Fields2 = lists:foldl(fun({Key, Value}, Acc) ->
case lists:member(Key, Categories) of
true -> maps:put(Key, Value, Acc);
false ->
case Value of
#schema_field{resolve = undefined} ->
Fun = category_disabled_fun(Key),
maps:put(Key, Value#schema_field{resolve = Fun}, Acc);
_ ->
maps:put(Key, Value, Acc)
end
end
end, #{}, maps:to_list(Fields)),
Schema2 = Schema#object_type{fields = Fields2},
Op#op{schema = Schema2};
parse_schema(_, Op, _) ->
Op.

-spec category_disabled_fun(binary()) -> resolver().
category_disabled_fun(Category) ->
Msg = <<"Category disabled">>,
Extra = #{category => Category},
fun(_, _, _, _) -> mongoose_graphql_helper:make_error(category_disabled, Msg, Extra) end.
51 changes: 16 additions & 35 deletions src/graphql/mongoose_graphql_cowboy_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,22 @@ auth_domain_admin(_, State) ->

run_request(#{document := undefined}, Req, State) ->
reply_error(make_error(decode, no_query_supplied), Req, State);
run_request(#{} = ReqCtx, Req, State) ->
fold(ReqCtx, {ReqCtx, Req, State}, [fun retrieve_category_from_ctx/2,
fun check_allowed_categories/2,
fun execute_request/2]).

fold({error, Error}, {_ReqCtx, Req, State}, _) ->
reply_error(Error, Req, State);
fold(Reply, {_ReqCtx, _Req, State}, []) ->
{stop, Reply, State};
fold(M, Data, [Step | Rest]) ->
fold(Step(M, Data), Data, Rest).
run_request(#{} = ReqCtx, Req, #{schema_endpoint := EpName,
authorized := AuthStatus} = State) ->
Ep = mongoose_graphql:get_endpoint(EpName),
Ctx = maps:get(schema_ctx, State, #{}),
AllowedCategories = maps:get(allowed_categories, State, []),
ReqCtx2 = ReqCtx#{authorized => AuthStatus,
ctx => Ctx#{method => http,
allowed_categories => AllowedCategories}},
case mongoose_graphql:execute(Ep, ReqCtx2) of
{ok, Response} ->
ResponseBody = mongoose_graphql_response:term_to_json(Response),
Req2 = cowboy_req:set_resp_body(ResponseBody, Req),
cowboy_req:reply(200, Req2);
{error, Reason} ->
reply_error(Reason, Req, State)
end.

retrieve_category_from_ctx(Ctx, _) ->
#{document := Doc} = Ctx,
Expand All @@ -199,30 +204,6 @@ retrieve_category_from_ctx(Ctx, _) ->
Error
end.


check_allowed_categories(Category, {_, _Req, State}) ->
AllowedCategories = maps:get(allowed_categories, State, []),
case AllowedCategories =:= [] orelse lists:member(Category, AllowedCategories) of
true ->
ok;
false ->
{error, {category_disabled, Category}}
end.

execute_request(_, {ReqCtx, Req, #{schema_endpoint := EpName,
authorized := AuthStatus} = State}) ->
Ep = mongoose_graphql:get_endpoint(EpName),
Ctx = maps:get(schema_ctx, State, #{}),
ReqCtx2 = ReqCtx#{authorized => AuthStatus, ctx => Ctx#{method => http}},
case mongoose_graphql:execute(Ep, ReqCtx2) of
{ok, Response} ->
ResponseBody = mongoose_graphql_response:term_to_json(Response),
Req2 = cowboy_req:set_resp_body(ResponseBody, Req),
cowboy_req:reply(200, Req2);
{error, Reason} ->
{error, Reason}
end.

gather(Req) ->
{ok, Body, Req2} = cowboy_req:read_body(Req),
Bindings = cowboy_req:bindings(Req2),
Expand Down

0 comments on commit bf8983e

Please sign in to comment.