Skip to content

Commit

Permalink
Apply review - refactor checking permissions for domain authorized re…
Browse files Browse the repository at this point in the history
…quest
  • Loading branch information
Premwoik committed Apr 29, 2022
1 parent 0ef4bf9 commit d602b0a
Showing 1 changed file with 48 additions and 40 deletions.
88 changes: 48 additions & 40 deletions src/graphql/mongoose_graphql_permissions.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@
user => jid:jid(),
admin => jid:jid(),
atom() => any()}.
-type fail_acc() :: #{path := [binary()],
type := atom(),
invalid := [binary()]}.
-type no_access_info() :: #{path := [binary()],
type := atom(),
invalid := [binary()]}.
-type field_check_result() :: ok | no_access_info().
-type document() :: #document{}.
-type definitions() :: [any()].

Expand Down Expand Up @@ -92,9 +93,9 @@ check_domain_authorized_request_permissions(OpName, Domain, Params, Definitions)
case Op of
[#op{selection_set = Set}] ->
case check_fields(#{domain => Domain}, Params, Set) of
[] ->
ok ->
ok;
[#{invalid := Args, path := Path, type := Type}| _] ->
#{invalid := Args, path := Path, type := Type} ->
OpName2 = op_name(OpName),
Error = {no_permissions, OpName2, Type, Args},
Path2 = lists:reverse([OpName2 | Path]),
Expand All @@ -106,56 +107,63 @@ check_domain_authorized_request_permissions(OpName, Domain, Params, Definitions)

% Internal

-spec check_fields(map(), map(), [field()]) -> [fail_acc()].
-spec check_fields(map(), map(), [any()]) -> ok | no_access_info().
check_fields(Ctx, Params, Fields) ->
% Return empty list when permissions are valid.
lists:flatten(lists:filtermap(fun(F) -> check_field(F, Ctx, Params) end, Fields)).
Fun = fun(F, ok) -> check_field(F, Ctx, Params);
(_, NoAccessInfo) -> NoAccessInfo
end,
lists:foldr(Fun, ok, Fields).

-spec check_field(field(), map(), params()) -> false | {true, fail_acc() | [fail_acc()]}.
-spec check_field(field(), map(), map()) -> field_check_result().
check_field(#field{id = Name, selection_set = Set, args = Args,
schema = #schema_field{directives = Directives}}, Ctx, Params) ->
schema = #schema_field{directives = Directives}}, Ctx, Params) ->
Args2 = maps:from_list([prepare_arg(ArgName, Type, Params) || {ArgName, Type} <- Args]),
Res =
case lists:filter(fun is_protected_directive/1, Directives) of
[#directive{} = Dir] ->
#{type := Type, args := PArgs} = protected_dir_args_to_map(Dir),
Acc = check_field_args(Type, Ctx, PArgs, Args2),
acc_path(name(Name), Acc);
[] ->
false
end,
check_field_type(Res, Ctx, Params, Set, Name).

check_field_type(false, Ctx, Params, Set, Name) ->
case check_fields(Ctx, Params, Set) of
[] -> false;
Invalid -> {true, [acc_path(Name, Acc) || Acc <- Invalid]}
end;
check_field_type(Acc, _, _, _, _) ->
Acc.
Res = check_field_args(Ctx, Args2, Directives),
Res2 = check_field_type(Res, Ctx, Params, Set),
add_path(Res2, name(Name));
check_field(_, _, _) -> ok.

-spec check_field_args(map(), map(), [graphql:directive()]) -> field_check_result().
check_field_args(Ctx, Args, Directives) ->
case lists:filter(fun is_protected_directive/1, Directives) of
[#directive{} = Dir] ->
#{type := {enum, Type}, args := PArgs} = protected_dir_args_to_map(Dir),
check_field_args(Type, Ctx, PArgs, Args);
[] ->
ok
end.

-spec check_field_args(binary(), map(), [binary()], map()) -> field_check_result().
check_field_args(<<"DOMAIN">>, #{domain := Domain}, ProtectedArgs, Args) ->
InvalidArgs =
lists:filter(fun(N) -> not arg_eq(maps:get(N, Args), Domain) end, ProtectedArgs),
make_result(InvalidArgs, domain);
check_field_args(<<"DEFAULT">>, _Ctx, _ProtectedArgs, _Args) ->
ok.

-spec check_field_type(field_check_result(), map(), map(), [any()]) -> field_check_result().
check_field_type(ok, Ctx, Params, Set) ->
check_fields(Ctx, Params, Set);
check_field_type(NoAccessInfo, _, _, _) ->
NoAccessInfo.

prepare_arg(ArgName, #{value := #var{id = Name}}, Vars) ->
{ArgName, maps:get(name(Name), Vars)};
prepare_arg(ArgName, #{value := Val}, _) ->
{ArgName, Val}.

check_field_args({enum, <<"DOMAIN">>}, #{domain := Domain}, ProtectedArgs, Params) ->
Res = lists:filter(fun(Arg) -> not arg_eq(maps:get(Arg, Params), Domain) end, ProtectedArgs),
acc(Res, domain);
check_field_args({enum, <<"DEFAULT">>}, _AuthCtx, _ProtectedArgs, _Args) ->
acc([], default).

arg_eq(Domain, Domain) -> true;
arg_eq(#jid{lserver = Domain}, Domain) -> true;
arg_eq(_, _) -> false.

acc([], Type) when is_atom(Type) -> false;
acc(Invalid, Type) when is_atom(Type) -> {true, #{type => Type, path => [], invalid => Invalid}}.
make_result([], _) ->
ok;
make_result(InvalidArgs, Type) when is_atom(Type) ->
#{type => Type, path => [], invalid => InvalidArgs}.

acc_path(_Field, false) -> false;
acc_path(Field, {true, Acc}) -> {true, acc_path(Field, Acc)};
acc_path(Field, #{path := Path} = Acc) ->
Acc#{path => [Field | Path]}.
add_path(ok, _) -> ok;
add_path(#{path := Path} = Acc, FieldName) ->
Acc#{path => [FieldName | Path]}.

protected_dir_args_to_map(#directive{args = Args}) ->
Default = #{type => {enum, <<"DEFAULT">>}, args => []},
Expand Down

0 comments on commit d602b0a

Please sign in to comment.