Merge pull request #8374 from JimMoen/fix-authn-http

fix(authn): authn http `is_superuser` field
This commit is contained in:
zhouzb 2022-07-01 21:56:27 +08:00 committed by GitHub
commit 8d4907eedd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 343 additions and 143 deletions

View File

@ -2,20 +2,24 @@
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"5.0.0", [{"5.0.0",
[{load_module,emqx_channel,brutal_purge,soft_purge,[]}, [{load_module,emqx_quic_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_config,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_schema,brutal_purge,soft_purge,[]}, {load_module,emqx_schema,brutal_purge,soft_purge,[]},
{load_module,emqx_release,brutal_purge,soft_purge,[]}, {load_module,emqx_release,brutal_purge,soft_purge,[]},
{load_module,emqx_authentication,brutal_purge,soft_purge,[]}, {load_module,emqx_authentication,brutal_purge,soft_purge,[]},
{load_module,emqx_metrics,brutal_purge,soft_purge,[]}, {load_module,emqx_metrics,brutal_purge,soft_purge,[]},
{add_module,emqx_exclusive_subscription}, {add_module,emqx_exclusive_subscription},
{apply, {emqx_exclusive_subscription, on_add_module, []}}, {apply,{emqx_exclusive_subscription,on_add_module,[]}},
{load_module,emqx_broker,brutal_purge,soft_purge,[]}, {load_module,emqx_broker,brutal_purge,soft_purge,[]},
{load_module,emqx_mqtt_caps,brutal_purge,soft_purge,[]}, {load_module,emqx_mqtt_caps,brutal_purge,soft_purge,[]},
{load_module,emqx_topic,brutal_purge,soft_purge,[]}, {load_module,emqx_topic,brutal_purge,soft_purge,[]},
{load_module,emqx_relup}]}, {load_module,emqx_relup}]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"5.0.0", [{"5.0.0",
[{load_module,emqx_channel,brutal_purge,soft_purge,[]}, [{load_module,emqx_quic_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_config,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_schema,brutal_purge,soft_purge,[]}, {load_module,emqx_schema,brutal_purge,soft_purge,[]},
{load_module,emqx_release,brutal_purge,soft_purge,[]}, {load_module,emqx_release,brutal_purge,soft_purge,[]},
{load_module,emqx_authentication,brutal_purge,soft_purge,[]}, {load_module,emqx_authentication,brutal_purge,soft_purge,[]},
@ -23,7 +27,7 @@
{load_module,emqx_broker,brutal_purge,soft_purge,[]}, {load_module,emqx_broker,brutal_purge,soft_purge,[]},
{load_module,emqx_mqtt_caps,brutal_purge,soft_purge,[]}, {load_module,emqx_mqtt_caps,brutal_purge,soft_purge,[]},
{load_module,emqx_topic,brutal_purge,soft_purge,[]}, {load_module,emqx_topic,brutal_purge,soft_purge,[]},
{apply, {emqx_exclusive_subscription, on_delete_module, []}}, {apply,{emqx_exclusive_subscription,on_delete_module,[]}},
{delete_module,emqx_exclusive_subscription}, {delete_module,emqx_exclusive_subscription},
{load_module,emqx_relup}]}, {load_module,emqx_relup}]},
{<<".*">>,[]}]}. {<<".*">>,[]}]}.

View File

@ -1,11 +1,13 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"0.1.0",[ [{"0.1.0",
[{load_module,emqx_authn_http,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]}, {load_module,emqx_authn_utils,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}] {load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}]},
}], {<<".*">>,[]}],
[{"0.1.0",[ [{"0.1.0",
[{load_module,emqx_authn_http,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]}, {load_module,emqx_authn_utils,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}] {load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}]},
}]}. {<<".*">>,[]}]}.

View File

@ -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}.

View File

@ -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) ->

View File

@ -37,6 +37,29 @@
protocol => mqtt protocol => mqtt
}). }).
-define(SERVER_RESPONSE_JSON(Result), ?SERVER_RESPONSE_JSON(Result, false)).
-define(SERVER_RESPONSE_JSON(Result, IsSuperuser),
jiffy:encode(#{
result => Result,
is_superuser => IsSuperuser
})
).
-define(SERVER_RESPONSE_URLENCODE(Result), ?SERVER_RESPONSE_URLENCODE(Result, false)).
-define(SERVER_RESPONSE_URLENCODE(Result, IsSuperuser),
list_to_binary(
"result=" ++
uri_encode(Result) ++ "&" ++
"is_superuser=" ++
uri_encode(IsSuperuser)
)
).
-define(EXCEPTION_ALLOW, ?EXCEPTION_ALLOW(false)).
-define(EXCEPTION_ALLOW(IsSuperuser), {ok, #{is_superuser := IsSuperuser}}).
-define(EXCEPTION_DENY, {error, not_authorized}).
-define(EXCEPTION_IGNORE, ignore).
all() -> all() ->
emqx_common_test_helpers:all(?MODULE). emqx_common_test_helpers:all(?MODULE).
@ -149,9 +172,12 @@ t_destroy(_Config) ->
{create_authenticator, ?GLOBAL, AuthConfig} {create_authenticator, ?GLOBAL, AuthConfig}
), ),
Headers = #{<<"content-type">> => <<"application/json">>},
Response = ?SERVER_RESPONSE_JSON(allow),
ok = emqx_authn_http_test_server:set_handler( ok = emqx_authn_http_test_server:set_handler(
fun(Req0, State) -> fun(Req0, State) ->
Req = cowboy_req:reply(200, Req0), Req = cowboy_req:reply(200, Headers, Response, Req0),
{ok, Req, State} {ok, Req, State}
end end
), ),
@ -161,9 +187,12 @@ t_destroy(_Config) ->
Credentials = maps:with([username, password], ?CREDENTIALS), Credentials = maps:with([username, password], ?CREDENTIALS),
{ok, _} = emqx_authn_http:authenticate( ?assertMatch(
Credentials, ?EXCEPTION_ALLOW,
State emqx_authn_http:authenticate(
Credentials,
State
)
), ),
emqx_authn_test_lib:delete_authenticators( emqx_authn_test_lib:delete_authenticators(
@ -173,7 +202,7 @@ t_destroy(_Config) ->
% Authenticator should not be usable anymore % Authenticator should not be usable anymore
?assertMatch( ?assertMatch(
ignore, ?EXCEPTION_IGNORE,
emqx_authn_http:authenticate( emqx_authn_http:authenticate(
Credentials, Credentials,
State State
@ -190,14 +219,20 @@ t_update(_Config) ->
{create_authenticator, ?GLOBAL, IncorrectConfig} {create_authenticator, ?GLOBAL, IncorrectConfig}
), ),
Headers = #{<<"content-type">> => <<"application/json">>},
Response = ?SERVER_RESPONSE_JSON(allow),
ok = emqx_authn_http_test_server:set_handler( ok = emqx_authn_http_test_server:set_handler(
fun(Req0, State) -> fun(Req0, State) ->
Req = cowboy_req:reply(200, Req0), Req = cowboy_req:reply(200, Headers, Response, Req0),
{ok, Req, State} {ok, Req, State}
end end
), ),
{error, not_authorized} = emqx_access_control:authenticate(?CREDENTIALS), ?assertMatch(
?EXCEPTION_DENY,
emqx_access_control:authenticate(?CREDENTIALS)
),
% We update with config with correct query, provider should update and work properly % We update with config with correct query, provider should update and work properly
{ok, _} = emqx:update_config( {ok, _} = emqx:update_config(
@ -205,7 +240,10 @@ t_update(_Config) ->
{update_authenticator, ?GLOBAL, <<"password_based:http">>, CorrectConfig} {update_authenticator, ?GLOBAL, <<"password_based:http">>, CorrectConfig}
), ),
{ok, _} = emqx_access_control:authenticate(?CREDENTIALS). ?assertMatch(
?EXCEPTION_ALLOW,
emqx_access_control:authenticate(?CREDENTIALS)
).
t_is_superuser(_Config) -> t_is_superuser(_Config) ->
Config = raw_http_auth_config(), Config = raw_http_auth_config(),
@ -215,33 +253,56 @@ t_is_superuser(_Config) ->
), ),
Checks = [ Checks = [
{json, <<"0">>, false}, %% {ContentType, ExpectedIsSuperuser, ResponseIsSuperuser}
{json, <<"">>, false}, %% Is Superuser
{json, null, false}, {json, true, <<"1">>},
{json, 0, false}, {json, true, 1},
{json, true, 123},
{json, true, <<"true">>},
{json, true, true},
{json, <<"1">>, true}, %% Not Superuser
{json, <<"val">>, true}, {json, false, <<"">>},
{json, 1, true}, {json, false, <<"0">>},
{json, 123, true}, {json, false, 0},
{json, false, null},
{json, false, undefined},
{json, false, <<"false">>},
{json, false, false},
{form, <<"0">>, false}, {json, false, <<"val">>},
{form, <<"">>, false},
{form, <<"1">>, true}, %% Is Superuser
{form, <<"val">>, true} {form, true, <<"1">>},
{form, true, 1},
{form, true, 123},
{form, true, <<"true">>},
{form, true, true},
%% Not Superuser
{form, false, <<"">>},
{form, false, <<"0">>},
{form, false, 0},
{form, false, null},
{form, false, undefined},
{form, false, <<"false">>},
{form, false, false},
{form, false, <<"val">>}
], ],
lists:foreach(fun test_is_superuser/1, Checks). lists:foreach(fun test_is_superuser/1, Checks).
test_is_superuser({Kind, Value, ExpectedValue}) -> test_is_superuser({Kind, ExpectedValue, ServerResponse}) ->
{ContentType, Res} = {ContentType, Res} =
case Kind of case Kind of
json -> json ->
{<<"application/json">>, jiffy:encode(#{is_superuser => Value})}; {<<"application/json; charset=utf-8">>,
?SERVER_RESPONSE_JSON(allow, ServerResponse)};
form -> form ->
{<<"application/x-www-form-urlencoded">>, {<<"application/x-www-form-urlencoded; charset=utf-8">>,
iolist_to_binary([<<"is_superuser=">>, Value])} ?SERVER_RESPONSE_URLENCODE(allow, ServerResponse)}
end, end,
ok = emqx_authn_http_test_server:set_handler( ok = emqx_authn_http_test_server:set_handler(
@ -257,10 +318,58 @@ test_is_superuser({Kind, Value, ExpectedValue}) ->
), ),
?assertMatch( ?assertMatch(
{ok, #{is_superuser := ExpectedValue}}, ?EXCEPTION_ALLOW(ExpectedValue),
emqx_access_control:authenticate(?CREDENTIALS) emqx_access_control:authenticate(?CREDENTIALS)
). ).
t_ignore_allow_deny(_Config) ->
Config = raw_http_auth_config(),
{ok, _} = emqx:update_config(
?PATH,
{create_authenticator, ?GLOBAL, Config}
),
Checks = [
%% only one chain, ignore by authn http and deny by default
{deny, ?SERVER_RESPONSE_JSON(ignore)},
{{allow, true}, ?SERVER_RESPONSE_JSON(allow, true)},
{{allow, false}, ?SERVER_RESPONSE_JSON(allow)},
{{allow, false}, ?SERVER_RESPONSE_JSON(allow, false)},
{deny, ?SERVER_RESPONSE_JSON(deny)},
{deny, ?SERVER_RESPONSE_JSON(deny, true)},
{deny, ?SERVER_RESPONSE_JSON(deny, false)}
],
lists:foreach(fun test_ignore_allow_deny/1, Checks).
test_ignore_allow_deny({ExpectedValue, ServerResponse}) ->
ok = emqx_authn_http_test_server:set_handler(
fun(Req0, State) ->
Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"application/json">>},
ServerResponse,
Req0
),
{ok, Req, State}
end
),
case ExpectedValue of
{allow, IsSuperuser} ->
?assertMatch(
?EXCEPTION_ALLOW(IsSuperuser),
emqx_access_control:authenticate(?CREDENTIALS)
);
deny ->
?assertMatch(
?EXCEPTION_DENY,
emqx_access_control:authenticate(?CREDENTIALS)
)
end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Helpers %% Helpers
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -287,11 +396,16 @@ samples() ->
password := <<"plain">> password := <<"plain">>
} = cowboy_req:match_qs([username, password], Req0), } = cowboy_req:match_qs([username, password], Req0),
Req = cowboy_req:reply(200, Req0), Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{result => allow, is_superuser => false}),
Req0
),
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{}, config_params => #{},
result => {ok, #{is_superuser => false}} result => {ok, #{is_superuser => false, user_property => #{}}}
}, },
%% get request with json body response %% get request with json body response
@ -300,7 +414,7 @@ samples() ->
Req = cowboy_req:reply( Req = cowboy_req:reply(
200, 200,
#{<<"content-type">> => <<"application/json">>}, #{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{is_superuser => true}), jiffy:encode(#{result => allow, is_superuser => true}),
Req0 Req0
), ),
{ok, Req, State} {ok, Req, State}
@ -318,7 +432,7 @@ samples() ->
<<"content-type">> => <<"content-type">> =>
<<"application/x-www-form-urlencoded">> <<"application/x-www-form-urlencoded">>
}, },
<<"is_superuser=true">>, <<"is_superuser=true&result=allow">>,
Req0 Req0
), ),
{ok, Req, State} {ok, Req, State}
@ -342,7 +456,8 @@ samples() ->
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{}, config_params => #{},
result => {ok, #{is_superuser => false}} %% only one chain, ignore by authn http and deny by default
result => {error, not_authorized}
}, },
%% simple post request, application/json %% simple post request, application/json
@ -353,14 +468,19 @@ samples() ->
<<"username">> := <<"plain">>, <<"username">> := <<"plain">>,
<<"password">> := <<"plain">> <<"password">> := <<"plain">>
} = jiffy:decode(RawBody, [return_maps]), } = jiffy:decode(RawBody, [return_maps]),
Req = cowboy_req:reply(200, Req1), Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{result => allow, is_superuser => false}),
Req1
),
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{ config_params => #{
<<"method">> => <<"post">>, <<"method">> => <<"post">>,
<<"headers">> => #{<<"content-type">> => <<"application/json">>} <<"headers">> => #{<<"content-type">> => <<"application/json">>}
}, },
result => {ok, #{is_superuser => false}} result => {ok, #{is_superuser => false, user_property => #{}}}
}, },
%% simple post request, application/x-www-form-urlencoded %% simple post request, application/x-www-form-urlencoded
@ -371,7 +491,12 @@ samples() ->
<<"username">> := <<"plain">>, <<"username">> := <<"plain">>,
<<"password">> := <<"plain">> <<"password">> := <<"plain">>
} = maps:from_list(PostVars), } = maps:from_list(PostVars),
Req = cowboy_req:reply(200, Req1), Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{result => allow, is_superuser => false}),
Req1
),
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{ config_params => #{
@ -381,15 +506,7 @@ samples() ->
<<"application/x-www-form-urlencoded">> <<"application/x-www-form-urlencoded">>
} }
}, },
result => {ok, #{is_superuser => false}} result => {ok, #{is_superuser => false, user_property => #{}}}
}#{
%% 204 code
handler => fun(Req0, State) ->
Req = cowboy_req:reply(204, Req0),
{ok, Req, State}
end,
config_params => #{},
result => {ok, #{is_superuser => false}}
}, },
%% simple post request for placeholders, application/json %% simple post request for placeholders, application/json
@ -402,7 +519,12 @@ samples() ->
<<"clientid">> := <<"clienta">>, <<"clientid">> := <<"clienta">>,
<<"peerhost">> := <<"127.0.0.1">> <<"peerhost">> := <<"127.0.0.1">>
} = jiffy:decode(RawBody, [return_maps]), } = jiffy:decode(RawBody, [return_maps]),
Req = cowboy_req:reply(200, Req1), Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{result => allow, is_superuser => false}),
Req1
),
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{ config_params => #{
@ -415,7 +537,7 @@ samples() ->
<<"peerhost">> => ?PH_PEERHOST <<"peerhost">> => ?PH_PEERHOST
} }
}, },
result => {ok, #{is_superuser => false}} result => {ok, #{is_superuser => false, user_property => #{}}}
}, },
%% custom headers %% custom headers
@ -426,6 +548,17 @@ samples() ->
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{}, config_params => #{},
%% only one chain, ignore by authn http and deny by default
result => {error, not_authorized}
},
%% 204 code
#{
handler => fun(Req0, State) ->
Req = cowboy_req:reply(204, Req0),
{ok, Req, State}
end,
config_params => #{},
result => {ok, #{is_superuser => false}} result => {ok, #{is_superuser => false}}
}, },
@ -436,6 +569,7 @@ samples() ->
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{}, config_params => #{},
%% only one chain, ignore by authn http and deny by default
result => {error, not_authorized} result => {error, not_authorized}
}, },
@ -446,6 +580,7 @@ samples() ->
{ok, Req, State} {ok, Req, State}
end, end,
config_params => #{}, config_params => #{},
%% only one chain, ignore by authn http and deny by default
result => {error, not_authorized} result => {error, not_authorized}
}, },
@ -456,6 +591,7 @@ samples() ->
{ok, Req0, State} {ok, Req0, State}
end, end,
config_params => #{}, config_params => #{},
%% only one chain, ignore by authn http and deny by default
result => {error, not_authorized} result => {error, not_authorized}
} }
]. ].
@ -465,3 +601,15 @@ start_apps(Apps) ->
stop_apps(Apps) -> stop_apps(Apps) ->
lists:foreach(fun application:stop/1, Apps). lists:foreach(fun application:stop/1, Apps).
uri_encode(T) ->
emqx_http_lib:uri_encode(to_list(T)).
to_list(A) when is_atom(A) ->
atom_to_list(A);
to_list(N) when is_integer(N) ->
integer_to_list(N);
to_list(B) when is_binary(B) ->
binary_to_list(B);
to_list(L) when is_list(L) ->
L.

View File

@ -167,6 +167,8 @@ cert_path(FileName) ->
cowboy_handler(Req0, State) -> cowboy_handler(Req0, State) ->
Req = cowboy_req:reply( Req = cowboy_req:reply(
200, 200,
#{<<"content-type">> => <<"application/json">>},
jiffy:encode(#{result => allow, is_superuser => false}),
Req0 Req0
), ),
{ok, Req, State}. {ok, Req, State}.

View File

@ -225,9 +225,10 @@ t_is_superuser(_Config) ->
{null, false}, {null, false},
{false, false}, {false, false},
{0, false}, {0, false},
{<<"val">>, false},
{<<"1">>, true}, {<<"1">>, true},
{<<"val">>, true}, {<<"123">>, true},
{1, true}, {1, true},
{123, true}, {123, true},
{true, true} {true, true}

View File

@ -243,7 +243,7 @@ t_is_superuser(_Config) ->
{is_superuser_str, "", false}, {is_superuser_str, "", false},
{is_superuser_str, null, false}, {is_superuser_str, null, false},
{is_superuser_str, "1", true}, {is_superuser_str, "1", true},
{is_superuser_str, "val", true}, {is_superuser_str, "val", false},
{is_superuser_int, 0, false}, {is_superuser_int, 0, false},
{is_superuser_int, null, false}, {is_superuser_int, null, false},

View File

@ -1,21 +1,19 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
{VSN, {VSN,
[ {"0.1.0", [{"0.1.0",
[ [{add_module, emqx_conf_proto_v2},
{add_module, emqx_conf_proto_v2}, {load_module,emqx_conf_schema,brutal_purge,soft_purge,[]},
{load_module,emqx_conf_schema,brutal_purge,soft_purge,[]}, {load_module,emqx_conf,brutal_purge,soft_purge,[]},
{load_module,emqx_conf,brutal_purge,soft_purge,[]}, {load_module,emqx_conf_app,brutal_purge,soft_purge,[]}
{load_module,emqx_conf_app,brutal_purge,soft_purge,[]} ]},
]}, {<<".*">>, []}
{<<".*">>, []}
], ],
[ {"0.1.0", [{"0.1.0",
[ [{delete_module, emqx_conf_proto_v2},
{delete_module, emqx_conf_proto_v2}, {load_module,emqx_conf_schema,brutal_purge,soft_purge,[]},
{load_module,emqx_conf_schema,brutal_purge,soft_purge,[]}, {load_module,emqx_conf,brutal_purge,soft_purge,[]},
{load_module,emqx_conf,brutal_purge,soft_purge,[]}, {load_module,emqx_conf_app,brutal_purge,soft_purge,[]}
{load_module,emqx_conf_app,brutal_purge,soft_purge,[]} ]},
]}, {<<".*">>, []}
{<<".*">>, []}
] ]
}. }.

View File

@ -1,13 +1,13 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"5.0.0", [ [{"5.0.0",
{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]}, [{load_module,emqx_dashboard,brutal_purge,soft_purge,[]},
{load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]} {load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]},
]}, {load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"5.0.0", [ [{"5.0.0",
{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]}, [{load_module,emqx_dashboard,brutal_purge,soft_purge,[]},
{load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]} {load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]},
]}, {load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}]}. {<<".*">>,[]}]}.

View File

@ -141,12 +141,14 @@ on_start_auth(authn_http) ->
%% set handler for test server %% set handler for test server
Handler = fun(Req0, State) -> Handler = fun(Req0, State) ->
ct:pal("Authn Req:~p~nState:~p~n", [Req0, State]), ct:pal("Authn Req:~p~nState:~p~n", [Req0, State]),
Headers = #{<<"content-type">> => <<"application/json">>},
Response = jiffy:encode(#{result => allow, is_superuser => false}),
case cowboy_req:match_qs([username, password], Req0) of case cowboy_req:match_qs([username, password], Req0) of
#{ #{
username := <<"admin">>, username := <<"admin">>,
password := <<"public">> password := <<"public">>
} -> } ->
Req = cowboy_req:reply(200, Req0); Req = cowboy_req:reply(200, Headers, Response, Req0);
_ -> _ ->
Req = cowboy_req:reply(400, Req0) Req = cowboy_req:reply(400, Req0)
end, end,

View File

@ -1,25 +1,25 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"5.0.0",[ [{"5.0.0",
{load_module,emqx_mgmt_cli,brutal_purge,soft_purge,[]}, [{load_module,emqx_mgmt_api_nodes,brutal_purge,soft_purge,[]},
{add_module,emqx_management_proto_v2}, {load_module,emqx_mgmt_cli,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_clients,brutal_purge,soft_purge,[]}, {add_module,emqx_management_proto_v2},
{load_module,emqx_mgmt_api_publish,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_clients,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_listeners,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_publish,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_configs,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_listeners,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_util,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_configs,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt,brutal_purge,soft_purge,[]} {load_module,emqx_mgmt_util,brutal_purge,soft_purge,[]},
]}, {load_module,emqx_mgmt,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"5.0.0",[ [{"5.0.0",
{load_module,emqx_mgmt_cli,brutal_purge,soft_purge,[]}, [{load_module,emqx_mgmt_api_nodes,brutal_purge,soft_purge,[]},
{delete_module,emqx_management_proto_v2}, {load_module,emqx_mgmt_cli,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_clients,brutal_purge,soft_purge,[]}, {delete_module,emqx_management_proto_v2},
{load_module,emqx_mgmt_api_publish,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_clients,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_listeners,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_publish,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_api_configs,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_listeners,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt_util,brutal_purge,soft_purge,[]}, {load_module,emqx_mgmt_api_configs,brutal_purge,soft_purge,[]},
{load_module,emqx_mgmt,brutal_purge,soft_purge,[]} {load_module,emqx_mgmt_util,brutal_purge,soft_purge,[]},
]}, {load_module,emqx_mgmt,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}]}. {<<".*">>,[]}]}.