From f0c783b260aa391a7ff699f431b6fff11a45918e Mon Sep 17 00:00:00 2001 From: JimMoen Date: Thu, 30 Jun 2022 14:54:10 +0800 Subject: [PATCH] test(authn): emqx_authn and gateway_athn http `content-type` --- .../emqx_authn/test/emqx_authn_http_SUITE.erl | 236 ++++++++++++++---- .../test/emqx_authn_https_SUITE.erl | 2 + .../test/emqx_gateway_auth_ct.erl | 4 +- 3 files changed, 197 insertions(+), 45 deletions(-) diff --git a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl index 4d17c7191..5384bcf6e 100644 --- a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl @@ -37,6 +37,29 @@ 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() -> emqx_common_test_helpers:all(?MODULE). @@ -149,9 +172,12 @@ t_destroy(_Config) -> {create_authenticator, ?GLOBAL, AuthConfig} ), + Headers = #{<<"content-type">> => <<"application/json">>}, + Response = ?SERVER_RESPONSE_JSON(allow), + ok = emqx_authn_http_test_server:set_handler( fun(Req0, State) -> - Req = cowboy_req:reply(200, Req0), + Req = cowboy_req:reply(200, Headers, Response, Req0), {ok, Req, State} end ), @@ -161,9 +187,12 @@ t_destroy(_Config) -> Credentials = maps:with([username, password], ?CREDENTIALS), - {ok, _} = emqx_authn_http:authenticate( - Credentials, - State + ?assertMatch( + ?EXCEPTION_ALLOW, + emqx_authn_http:authenticate( + Credentials, + State + ) ), emqx_authn_test_lib:delete_authenticators( @@ -173,7 +202,7 @@ t_destroy(_Config) -> % Authenticator should not be usable anymore ?assertMatch( - ignore, + ?EXCEPTION_IGNORE, emqx_authn_http:authenticate( Credentials, State @@ -190,14 +219,20 @@ t_update(_Config) -> {create_authenticator, ?GLOBAL, IncorrectConfig} ), + Headers = #{<<"content-type">> => <<"application/json">>}, + Response = ?SERVER_RESPONSE_JSON(allow), + ok = emqx_authn_http_test_server:set_handler( fun(Req0, State) -> - Req = cowboy_req:reply(200, Req0), + Req = cowboy_req:reply(200, Headers, Response, Req0), {ok, Req, State} 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 {ok, _} = emqx:update_config( @@ -205,7 +240,10 @@ t_update(_Config) -> {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) -> Config = raw_http_auth_config(), @@ -215,33 +253,56 @@ t_is_superuser(_Config) -> ), Checks = [ - {json, <<"0">>, false}, - {json, <<"">>, false}, - {json, null, false}, - {json, 0, false}, + %% {ContentType, ExpectedIsSuperuser, ResponseIsSuperuser} + %% Is Superuser + {json, true, <<"1">>}, + {json, true, 1}, + {json, true, 123}, + {json, true, <<"true">>}, + {json, true, true}, - {json, <<"1">>, true}, - {json, <<"val">>, true}, - {json, 1, true}, - {json, 123, true}, + %% Not Superuser + {json, false, <<"">>}, + {json, false, <<"0">>}, + {json, false, 0}, + {json, false, null}, + {json, false, undefined}, + {json, false, <<"false">>}, + {json, false, false}, - {form, <<"0">>, false}, - {form, <<"">>, false}, + {json, false, <<"val">>}, - {form, <<"1">>, true}, - {form, <<"val">>, true} + %% Is Superuser + {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). -test_is_superuser({Kind, Value, ExpectedValue}) -> +test_is_superuser({Kind, ExpectedValue, ServerResponse}) -> {ContentType, Res} = case Kind of json -> - {<<"application/json">>, jiffy:encode(#{is_superuser => Value})}; + {<<"application/json; charset=utf-8">>, + ?SERVER_RESPONSE_JSON(allow, ServerResponse)}; form -> - {<<"application/x-www-form-urlencoded">>, - iolist_to_binary([<<"is_superuser=">>, Value])} + {<<"application/x-www-form-urlencoded; charset=utf-8">>, + ?SERVER_RESPONSE_URLENCODE(allow, ServerResponse)} end, ok = emqx_authn_http_test_server:set_handler( @@ -257,10 +318,58 @@ test_is_superuser({Kind, Value, ExpectedValue}) -> ), ?assertMatch( - {ok, #{is_superuser := ExpectedValue}}, + ?EXCEPTION_ALLOW(ExpectedValue), 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 %%------------------------------------------------------------------------------ @@ -287,11 +396,16 @@ samples() -> password := <<"plain">> } = 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} end, config_params => #{}, - result => {ok, #{is_superuser => false}} + result => {ok, #{is_superuser => false, user_property => #{}}} }, %% get request with json body response @@ -300,7 +414,7 @@ samples() -> Req = cowboy_req:reply( 200, #{<<"content-type">> => <<"application/json">>}, - jiffy:encode(#{is_superuser => true}), + jiffy:encode(#{result => allow, is_superuser => true}), Req0 ), {ok, Req, State} @@ -318,7 +432,7 @@ samples() -> <<"content-type">> => <<"application/x-www-form-urlencoded">> }, - <<"is_superuser=true">>, + <<"is_superuser=true&result=allow">>, Req0 ), {ok, Req, State} @@ -342,7 +456,8 @@ samples() -> {ok, Req, State} end, 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 @@ -353,14 +468,19 @@ samples() -> <<"username">> := <<"plain">>, <<"password">> := <<"plain">> } = 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} end, config_params => #{ <<"method">> => <<"post">>, <<"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 @@ -371,7 +491,12 @@ samples() -> <<"username">> := <<"plain">>, <<"password">> := <<"plain">> } = 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} end, config_params => #{ @@ -381,15 +506,7 @@ samples() -> <<"application/x-www-form-urlencoded">> } }, - result => {ok, #{is_superuser => false}} - }#{ - %% 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, user_property => #{}}} }, %% simple post request for placeholders, application/json @@ -402,7 +519,12 @@ samples() -> <<"clientid">> := <<"clienta">>, <<"peerhost">> := <<"127.0.0.1">> } = 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} end, config_params => #{ @@ -415,7 +537,7 @@ samples() -> <<"peerhost">> => ?PH_PEERHOST } }, - result => {ok, #{is_superuser => false}} + result => {ok, #{is_superuser => false, user_property => #{}}} }, %% custom headers @@ -426,6 +548,17 @@ samples() -> {ok, Req, State} end, 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}} }, @@ -436,6 +569,7 @@ samples() -> {ok, Req, State} end, config_params => #{}, + %% only one chain, ignore by authn http and deny by default result => {error, not_authorized} }, @@ -446,6 +580,7 @@ samples() -> {ok, Req, State} end, config_params => #{}, + %% only one chain, ignore by authn http and deny by default result => {error, not_authorized} }, @@ -456,6 +591,7 @@ samples() -> {ok, Req0, State} end, config_params => #{}, + %% only one chain, ignore by authn http and deny by default result => {error, not_authorized} } ]. @@ -465,3 +601,15 @@ start_apps(Apps) -> stop_apps(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. diff --git a/apps/emqx_authn/test/emqx_authn_https_SUITE.erl b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl index ae4508967..7aa40f909 100644 --- a/apps/emqx_authn/test/emqx_authn_https_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl @@ -167,6 +167,8 @@ cert_path(FileName) -> cowboy_handler(Req0, State) -> Req = cowboy_req:reply( 200, + #{<<"content-type">> => <<"application/json">>}, + jiffy:encode(#{result => allow, is_superuser => false}), Req0 ), {ok, Req, State}. diff --git a/apps/emqx_gateway/test/emqx_gateway_auth_ct.erl b/apps/emqx_gateway/test/emqx_gateway_auth_ct.erl index 7fec71ae3..14b3d463e 100644 --- a/apps/emqx_gateway/test/emqx_gateway_auth_ct.erl +++ b/apps/emqx_gateway/test/emqx_gateway_auth_ct.erl @@ -141,12 +141,14 @@ on_start_auth(authn_http) -> %% set handler for test server Handler = fun(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 #{ username := <<"admin">>, password := <<"public">> } -> - Req = cowboy_req:reply(200, Req0); + Req = cowboy_req:reply(200, Headers, Response, Req0); _ -> Req = cowboy_req:reply(400, Req0) end,