fix(authn): authn http `is_superuser` field
only support true|false|[num] and for [num]: zero => false | non-zero => true other type and string will fallback to default vaule => false i.e null|undefined|[empty string]
This commit is contained in:
parent
7d46e8671e
commit
b88796518e
|
@ -135,6 +135,18 @@ render_sql_params(ParamList, Credential) ->
|
||||||
#{return => rawlist, var_trans => fun handle_sql_var/2}
|
#{return => rawlist, var_trans => fun handle_sql_var/2}
|
||||||
).
|
).
|
||||||
|
|
||||||
|
%% true
|
||||||
|
is_superuser(#{<<"is_superuser">> := <<"true">>}) ->
|
||||||
|
#{is_superuser => true};
|
||||||
|
is_superuser(#{<<"is_superuser">> := true}) ->
|
||||||
|
#{is_superuser => true};
|
||||||
|
is_superuser(#{<<"is_superuser">> := <<"1">>}) ->
|
||||||
|
#{is_superuser => true};
|
||||||
|
is_superuser(#{<<"is_superuser">> := I}) when
|
||||||
|
is_integer(I) andalso I >= 1
|
||||||
|
->
|
||||||
|
#{is_superuser => true};
|
||||||
|
%% false
|
||||||
is_superuser(#{<<"is_superuser">> := <<"">>}) ->
|
is_superuser(#{<<"is_superuser">> := <<"">>}) ->
|
||||||
#{is_superuser => false};
|
#{is_superuser => false};
|
||||||
is_superuser(#{<<"is_superuser">> := <<"0">>}) ->
|
is_superuser(#{<<"is_superuser">> := <<"0">>}) ->
|
||||||
|
@ -145,10 +157,25 @@ is_superuser(#{<<"is_superuser">> := null}) ->
|
||||||
#{is_superuser => false};
|
#{is_superuser => false};
|
||||||
is_superuser(#{<<"is_superuser">> := undefined}) ->
|
is_superuser(#{<<"is_superuser">> := undefined}) ->
|
||||||
#{is_superuser => false};
|
#{is_superuser => false};
|
||||||
|
is_superuser(#{<<"is_superuser">> := <<"false">>}) ->
|
||||||
|
#{is_superuser => false};
|
||||||
is_superuser(#{<<"is_superuser">> := false}) ->
|
is_superuser(#{<<"is_superuser">> := false}) ->
|
||||||
#{is_superuser => false};
|
#{is_superuser => false};
|
||||||
|
is_superuser(#{<<"is_superuser">> := MaybeBinInt}) when
|
||||||
|
is_binary(MaybeBinInt)
|
||||||
|
->
|
||||||
|
try binary_to_integer(MaybeBinInt) of
|
||||||
|
Int when Int >= 1 ->
|
||||||
|
#{is_superuser => true};
|
||||||
|
Int when Int =< 0 ->
|
||||||
|
#{is_superuser => false}
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
#{is_superuser => false}
|
||||||
|
end;
|
||||||
|
%% fallback to default
|
||||||
is_superuser(#{<<"is_superuser">> := _}) ->
|
is_superuser(#{<<"is_superuser">> := _}) ->
|
||||||
#{is_superuser => true};
|
#{is_superuser => false};
|
||||||
is_superuser(#{}) ->
|
is_superuser(#{}) ->
|
||||||
#{is_superuser => false}.
|
#{is_superuser => false}.
|
||||||
|
|
||||||
|
|
|
@ -191,48 +191,21 @@ authenticate(
|
||||||
case emqx_resource:query(ResourceId, {Method, Request, RequestTimeout}) of
|
case emqx_resource:query(ResourceId, {Method, Request, RequestTimeout}) of
|
||||||
{ok, 204, _Headers} ->
|
{ok, 204, _Headers} ->
|
||||||
{ok, #{is_superuser => false}};
|
{ok, #{is_superuser => false}};
|
||||||
{ok, 200, _Headers} ->
|
|
||||||
{ok, #{is_superuser => false}};
|
|
||||||
{ok, 200, Headers, Body} ->
|
{ok, 200, Headers, Body} ->
|
||||||
ContentType = proplists:get_value(<<"content-type">>, Headers, <<"application/json">>),
|
handle_response(Headers, Body);
|
||||||
case safely_parse_body(ContentType, Body) of
|
{ok, _StatusCode, _Headers} = Response ->
|
||||||
{ok, NBody} ->
|
log_response(ResourceId, Response),
|
||||||
%% TODO: Return by user property
|
ignore;
|
||||||
UserProperty = maps:remove(<<"is_superuser">>, NBody),
|
{ok, _StatusCode, _Headers, _Body} = Response ->
|
||||||
IsSuperuser = emqx_authn_utils:is_superuser(NBody),
|
log_response(ResourceId, Response),
|
||||||
{ok, IsSuperuser#{user_property => UserProperty}};
|
ignore;
|
||||||
{error, _Reason} ->
|
|
||||||
{ok, #{is_superuser => false}}
|
|
||||||
end;
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, #{
|
?SLOG(error, #{
|
||||||
msg => "http_server_query_failed",
|
msg => "http_server_query_failed",
|
||||||
resource => ResourceId,
|
resource => ResourceId,
|
||||||
reason => Reason
|
reason => Reason
|
||||||
}),
|
}),
|
||||||
ignore;
|
ignore
|
||||||
Other ->
|
|
||||||
Output = may_append_body(#{resource => ResourceId}, Other),
|
|
||||||
case erlang:element(2, Other) of
|
|
||||||
Code5xx when Code5xx >= 500 andalso Code5xx < 600 ->
|
|
||||||
?SLOG(error, Output#{
|
|
||||||
msg => "http_server_error",
|
|
||||||
code => Code5xx
|
|
||||||
}),
|
|
||||||
ignore;
|
|
||||||
Code4xx when Code4xx >= 400 andalso Code4xx < 500 ->
|
|
||||||
?SLOG(warning, Output#{
|
|
||||||
msg => "refused_by_http_server",
|
|
||||||
code => Code4xx
|
|
||||||
}),
|
|
||||||
{error, not_authorized};
|
|
||||||
OtherCode ->
|
|
||||||
?SLOG(error, Output#{
|
|
||||||
msg => "undesired_response_code",
|
|
||||||
code => OtherCode
|
|
||||||
}),
|
|
||||||
ignore
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
destroy(#{resource_id := ResourceId}) ->
|
destroy(#{resource_id := ResourceId}) ->
|
||||||
|
@ -366,20 +339,43 @@ qs([{K, V} | More], Acc) ->
|
||||||
serialize_body(<<"application/json">>, Body) ->
|
serialize_body(<<"application/json">>, Body) ->
|
||||||
emqx_json:encode(Body);
|
emqx_json:encode(Body);
|
||||||
serialize_body(<<"application/x-www-form-urlencoded">>, Body) ->
|
serialize_body(<<"application/x-www-form-urlencoded">>, Body) ->
|
||||||
qs(Body).
|
qs(maps:to_list(Body)).
|
||||||
|
|
||||||
|
handle_response(Headers, Body) ->
|
||||||
|
ContentType = proplists:get_value(<<"content-type">>, Headers),
|
||||||
|
case safely_parse_body(ContentType, Body) of
|
||||||
|
{ok, NBody} ->
|
||||||
|
case maps:get(<<"result">>, NBody, <<"ignore">>) of
|
||||||
|
<<"allow">> ->
|
||||||
|
Res = emqx_authn_utils:is_superuser(NBody),
|
||||||
|
%% TODO: Return by user property
|
||||||
|
{ok, Res#{user_property => maps:get(<<"user_property">>, NBody, #{})}};
|
||||||
|
<<"deny">> ->
|
||||||
|
{error, not_authorized};
|
||||||
|
<<"ignore">> ->
|
||||||
|
ignore;
|
||||||
|
_ ->
|
||||||
|
ignore
|
||||||
|
end;
|
||||||
|
{error, _Reason} ->
|
||||||
|
ignore
|
||||||
|
end.
|
||||||
|
|
||||||
safely_parse_body(ContentType, Body) ->
|
safely_parse_body(ContentType, Body) ->
|
||||||
try parse_body(ContentType, Body) of
|
try
|
||||||
Result -> Result
|
parse_body(ContentType, Body)
|
||||||
catch
|
catch
|
||||||
_Class:_Reason ->
|
_Class:_Reason ->
|
||||||
{error, invalid_body}
|
{error, invalid_body}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_body(<<"application/json">>, Body) ->
|
parse_body(<<"application/json", _/binary>>, Body) ->
|
||||||
{ok, emqx_json:decode(Body, [return_maps])};
|
{ok, emqx_json:decode(Body, [return_maps])};
|
||||||
parse_body(<<"application/x-www-form-urlencoded">>, Body) ->
|
parse_body(<<"application/x-www-form-urlencoded", _/binary>>, Body) ->
|
||||||
{ok, maps:from_list(cow_qs:parse_qs(Body))};
|
Flags = [<<"result">>, <<"is_superuser">>],
|
||||||
|
RawMap = maps:from_list(cow_qs:parse_qs(Body)),
|
||||||
|
NBody = maps:with(Flags, RawMap),
|
||||||
|
{ok, NBody};
|
||||||
parse_body(ContentType, _) ->
|
parse_body(ContentType, _) ->
|
||||||
{error, {unsupported_content_type, ContentType}}.
|
{error, {unsupported_content_type, ContentType}}.
|
||||||
|
|
||||||
|
@ -395,6 +391,26 @@ encode_path(Path) ->
|
||||||
Parts = string:split(Path, "/", all),
|
Parts = string:split(Path, "/", all),
|
||||||
lists:flatten(["/" ++ Part || Part <- lists:map(fun uri_encode/1, Parts)]).
|
lists:flatten(["/" ++ Part || Part <- lists:map(fun uri_encode/1, Parts)]).
|
||||||
|
|
||||||
|
log_response(ResourceId, Other) ->
|
||||||
|
Output = may_append_body(#{resource => ResourceId}, Other),
|
||||||
|
case erlang:element(2, Other) of
|
||||||
|
Code5xx when Code5xx >= 500 andalso Code5xx < 600 ->
|
||||||
|
?SLOG(error, Output#{
|
||||||
|
msg => "http_server_error",
|
||||||
|
code => Code5xx
|
||||||
|
});
|
||||||
|
Code4xx when Code4xx >= 400 andalso Code4xx < 500 ->
|
||||||
|
?SLOG(warning, Output#{
|
||||||
|
msg => "refused_by_http_server",
|
||||||
|
code => Code4xx
|
||||||
|
});
|
||||||
|
OtherCode ->
|
||||||
|
?SLOG(error, Output#{
|
||||||
|
msg => "undesired_response_code",
|
||||||
|
code => OtherCode
|
||||||
|
})
|
||||||
|
end.
|
||||||
|
|
||||||
to_list(A) when is_atom(A) ->
|
to_list(A) when is_atom(A) ->
|
||||||
atom_to_list(A);
|
atom_to_list(A);
|
||||||
to_list(B) when is_binary(B) ->
|
to_list(B) when is_binary(B) ->
|
||||||
|
|
Loading…
Reference in New Issue