test(gateway): integration stomp && exproto test with http authn

This commit is contained in:
firest 2022-04-02 13:54:59 +08:00
parent 25ebe2535e
commit 0db6b8d47e
4 changed files with 229 additions and 107 deletions

View File

@ -106,19 +106,19 @@ t_connection(_) ->
t_publish(_) -> t_publish(_) ->
Action = fun(Channel, Token) -> Action = fun(Channel, Token) ->
Topic = <<"/abc">>, Topic = <<"/abc">>,
Payload = <<"123">>, Payload = <<"123">>,
TopicStr = binary_to_list(Topic), TopicStr = binary_to_list(Topic),
URI = ?PS_PREFIX ++ TopicStr ++ "?clientid=client1&token=" ++ Token, URI = ?PS_PREFIX ++ TopicStr ++ "?clientid=client1&token=" ++ Token,
%% Sub topic first %% Sub topic first
emqx:subscribe(Topic), emqx:subscribe(Topic),
Req = make_req(post, Payload), Req = make_req(post, Payload),
{ok, changed, _} = do_request(Channel, URI, Req), {ok, changed, _} = do_request(Channel, URI, Req),
receive receive
{deliver, Topic, Msg} -> {deliver, Topic, Msg} ->
?assertEqual(Topic, Msg#message.topic), ?assertEqual(Topic, Msg#message.topic),
?assertEqual(Payload, Msg#message.payload) ?assertEqual(Payload, Msg#message.payload)

View File

@ -40,6 +40,19 @@
-define(TCPOPTS, [binary, {active, false}]). -define(TCPOPTS, [binary, {active, false}]).
-define(DTLSOPTS, [binary, {active, false}, {protocol, dtls}]). -define(DTLSOPTS, [binary, {active, false}, {protocol, dtls}]).
%%--------------------------------------------------------------------
-define(CONF_DEFAULT,
<<"\n"
"gateway.exproto {\n"
" server.bind = 9100,\n"
" handler.address = \"http://127.0.0.1:9001\"\n"
" listeners.tcp.default {\n"
" bind = 7993,\n"
" acceptors = 8\n"
" }\n"
"}\n">>
).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Setups %% Setups
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -84,6 +97,9 @@ listener_confs(Type) ->
Default = #{bind => 7993, acceptors => 8}, Default = #{bind => 7993, acceptors => 8},
#{Type => #{'default' => maps:merge(Default, socketopts(Type))}}. #{Type => #{'default' => maps:merge(Default, socketopts(Type))}}.
default_config() ->
?CONF_DEFAULT.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Tests cases %% Tests cases
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------

View File

@ -29,31 +29,45 @@
-define(HTTP_PORT, 33333). -define(HTTP_PORT, 33333).
-define(HTTP_PATH, "/auth"). -define(HTTP_PATH, "/auth").
-define(CREDENTIALS, #{username => <<"admin">>,
password => <<"public">>,
listener => 'tcp:default',
protocol => mqtt
}).
-define(checkMatch(Guard), -define(checkMatch(Guard),
(fun (Expr) -> (fun(Expr) ->
case (Expr) of case (Expr) of
Guard -> ok; Guard ->
X__V -> ok;
erlang:error({assertMatch, X__V ->
[{module, ?MODULE}, erlang:error(
{line, ?LINE}, {assertMatch, [
{expression, (??Expr)}, {module, ?MODULE},
{pattern, (??Guard)}, {line, ?LINE},
{value, X__V}]}) {expression, (??Expr)},
end {pattern, (??Guard)},
end)). {value, X__V}
]}
)
end
end)
).
-define(FUNCTOR(Expr), fun() -> Expr end). -define(FUNCTOR(Expr), fun() -> Expr end).
-define(FUNCTOR(Arg, Expr), fun(Arg) -> Expr end). -define(FUNCTOR(Arg, Expr), fun(Arg) -> Expr end).
-define(PROTOCOLS, [coap, lwm2m, 'mqtt-sn'] ). -define(PROTOCOLS, [coap, lwm2m, 'mqtt-sn', stomp, exproto]).
-define(CONFS, [emqx_coap_SUITE, emqx_lwm2m_SUITE, emqx_sn_protocol_SUITE]).
-define(CASES, [fun case_coap/0, fun case_lwm2m/0, fun case_emqx_sn/0, fun case_stomp/0]). -define(CONFS, [
emqx_coap_SUITE,
emqx_lwm2m_SUITE,
emqx_sn_protocol_SUITE,
emqx_stomp_SUITE,
emqx_exproto_SUITE
]).
-define(CASES, [
fun case_coap/0,
fun case_lwm2m/0,
fun case_emqx_sn/0,
fun case_stomp/0,
fun case_exproto/0
]).
-define(AUTHNS, [fun set_http_authn/1]). -define(AUTHNS, [fun set_http_authn/1]).
-type auth_controller() :: fun((start | stop) -> ok). -type auth_controller() :: fun((start | stop) -> ok).
@ -94,19 +108,21 @@ t_authn(_) ->
case_coap() -> case_coap() ->
Login = fun(URI, Checker) -> Login = fun(URI, Checker) ->
Action = fun(Channel) -> Action = fun(Channel) ->
Req = emqx_coap_SUITE:make_req(post), Req = emqx_coap_SUITE:make_req(post),
Checker(emqx_coap_SUITE:do_request(Channel, URI, Req)) Checker(emqx_coap_SUITE:do_request(Channel, URI, Req))
end, end,
emqx_coap_SUITE:do(Action) emqx_coap_SUITE:do(Action)
end, end,
Prefix = emqx_coap_SUITE:mqtt_prefix(), Prefix = emqx_coap_SUITE:mqtt_prefix(),
RightUrl = Prefix ++ RightUrl =
"/connection?clientid=client1&username=admin&password=public", Prefix ++
"/connection?clientid=client1&username=admin&password=public",
Login(RightUrl, ?checkMatch({ok, created, _Data})), Login(RightUrl, ?checkMatch({ok, created, _Data})),
LeftUrl = Prefix ++ LeftUrl =
"/connection?clientid=client1&username=bad&password=bad", Prefix ++
"/connection?clientid=client1&username=bad&password=bad",
Login(LeftUrl, ?checkMatch({error, bad_request, _Data})), Login(LeftUrl, ?checkMatch({error, bad_request, _Data})),
ok. ok.
@ -118,28 +134,33 @@ case_lwm2m() ->
Epn = "urn:oma:lwm2m:oma:3", Epn = "urn:oma:lwm2m:oma:3",
Port = emqx_lwm2m_SUITE:default_port(), Port = emqx_lwm2m_SUITE:default_port(),
Login = fun(URI, Checker) -> Login = fun(URI, Checker) ->
with_resource(?FUNCTOR(gen_udp:open(0, [binary, {active, false}])), with_resource(
?FUNCTOR(Socket, gen_udp:close(Socket)), ?FUNCTOR(gen_udp:open(0, [binary, {active, false}])),
fun(Socket) -> ?FUNCTOR(Socket, gen_udp:close(Socket)),
Mod:test_send_coap_request( fun(Socket) ->
Socket, Mod:test_send_coap_request(
post, Socket,
Mod:sprintf(URI, [Port, Epn]), post,
#coap_content{content_format = <<"text/plain">>, Mod:sprintf(URI, [Port, Epn]),
payload = <<"</1>, </2>, </3>, </4>, </5>">>}, #coap_content{
[], content_format = <<"text/plain">>,
MsgId), payload = <<"</1>, </2>, </3>, </4>, </5>">>
},
[],
MsgId
),
Checker(Mod:test_recv_coap_response(Socket)) Checker(Mod:test_recv_coap_response(Socket))
end) end
end, )
end,
MakeCheker = fun(Type, Method) -> MakeCheker = fun(Type, Method) ->
fun(Msg) -> fun(Msg) ->
?assertEqual(Type, emqx_coap_SUITE:get_field(type, Msg)), ?assertEqual(Type, emqx_coap_SUITE:get_field(type, Msg)),
?assertEqual(Method, emqx_coap_SUITE:get_field(method, Msg)) ?assertEqual(Method, emqx_coap_SUITE:get_field(method, Msg))
end end
end, end,
RightUrl = "coap://127.0.0.1:~b/rd?ep=~ts&lt=345&lwm2m=1&imei=admin&password=public", RightUrl = "coap://127.0.0.1:~b/rd?ep=~ts&lt=345&lwm2m=1&imei=admin&password=public",
Login(RightUrl, MakeCheker(ack, {ok, created})), Login(RightUrl, MakeCheker(ack, {ok, created})),
@ -156,23 +177,94 @@ case_lwm2m() ->
case_emqx_sn() -> case_emqx_sn() ->
Mod = emqx_sn_protocol_SUITE, Mod = emqx_sn_protocol_SUITE,
Login = fun(Expect) -> Login = fun(Expect) ->
with_resource(?FUNCTOR(gen_udp:open(0, [binary])), with_resource(
?FUNCTOR(Socket, gen_udp:close(Socket)), ?FUNCTOR(gen_udp:open(0, [binary])),
fun(Socket) -> ?FUNCTOR(Socket, gen_udp:close(Socket)),
Mod:send_connect_msg(Socket, <<"client_id_test1">>), fun(Socket) ->
?assertEqual(Expect, Mod:receive_response(Socket)) Mod:send_connect_msg(Socket, <<"client_id_test1">>),
end) ?assertEqual(Expect, Mod:receive_response(Socket))
end, end
)
end,
Login(<<>>), Login(<<>>),
RawCfg = emqx_conf:get_raw([gateway, mqttsn], #{}), RawCfg = emqx_conf:get_raw([gateway, mqttsn], #{}),
NewCfg = RawCfg#{<<"clientinfo_override">> => #{<<"username">> => <<"admin">>, NewCfg = RawCfg#{
<<"password">> => <<"public">>}}, <<"clientinfo_override">> => #{
<<"username">> => <<"admin">>,
<<"password">> => <<"public">>
}
},
emqx_gateway_conf:update_gateway(mqttsn, NewCfg), emqx_gateway_conf:update_gateway(mqttsn, NewCfg),
Login(<<3, ?SN_CONNACK, 0>>), Login(<<3, ?SN_CONNACK, 0>>),
ok. ok.
case_stomp() -> case_stomp() ->
Mod = emqx_stomp_SUITE,
Login = fun(Username, Password, Checker) ->
Fun = fun(Sock) ->
gen_tcp:send(
Sock,
Mod:serialize(
<<"CONNECT">>,
[
{<<"accept-version">>, Mod:stomp_ver()},
{<<"host">>, <<"127.0.0.1:61613">>},
{<<"login">>, Username},
{<<"passcode">>, Password},
{<<"heart-beat">>, <<"1000,2000">>}
]
)
),
{ok, Data} = gen_tcp:recv(Sock, 0),
{ok, Frame, _, _} = Mod:parse(Data),
Checker(Frame)
end,
Mod:with_connection(Fun)
end,
Login(
<<"admin">>,
<<"public">>,
?FUNCTOR(
Frame,
?assertEqual(<<"CONNECTED">>, Mod:get_field(command, Frame))
)
),
Login(<<"bad">>, <<"bad">>, fun(Frame) ->
?assertEqual(<<"ERROR">>, Mod:get_field(command, Frame)),
?assertEqual(<<"Login Failed: not_authorized">>, Mod:get_field(body, Frame))
end),
ok.
case_exproto() ->
Mod = emqx_exproto_SUITE,
SvrMod = emqx_exproto_echo_svr,
Svrs = SvrMod:start(),
Login = fun(Username, Password, Expect) ->
with_resource(
?FUNCTOR(Mod:open(tcp)),
?FUNCTOR(Sock, Mod:close(Sock)),
fun(Sock) ->
Client = #{
proto_name => <<"demo">>,
proto_ver => <<"v0.1">>,
clientid => <<"test_client_1">>,
username => Username
},
ConnBin = SvrMod:frame_connect(Client, Password),
Mod:send(Sock, ConnBin),
{ok, Recv} = Mod:recv(Sock, 5000),
C = ?FUNCTOR(Bin, emqx_json:decode(Bin, [return_maps])),
?assertEqual(C(Expect), C(Recv))
end
)
end,
Login(<<"admin">>, <<"public">>, SvrMod:frame_connack(0)),
Login(<<"bad">>, <<"bad">>, SvrMod:frame_connack(1)),
SvrMod:stop(Svrs),
ok. ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -181,15 +273,15 @@ case_stomp() ->
raw_http_auth_config() -> raw_http_auth_config() ->
#{ #{
mechanism => <<"password_based">>, mechanism => <<"password_based">>,
enable => <<"true">>, enable => <<"true">>,
backend => <<"http">>, backend => <<"http">>,
method => <<"get">>, method => <<"get">>,
url => <<"http://127.0.0.1:33333/auth">>, url => <<"http://127.0.0.1:33333/auth">>,
body => #{<<"username">> => ?PH_USERNAME, <<"password">> => ?PH_PASSWORD}, body => #{<<"username">> => ?PH_USERNAME, <<"password">> => ?PH_PASSWORD},
headers => #{<<"X-Test-Header">> => <<"Test Value">>} headers => #{<<"X-Test-Header">> => <<"Test Value">>}
}. }.
set_http_authn(start) -> set_http_authn(start) ->
{ok, _} = emqx_authn_http_test_server:start_link(?HTTP_PORT, ?HTTP_PATH), {ok, _} = emqx_authn_http_test_server:start_link(?HTTP_PORT, ?HTTP_PATH),
@ -197,37 +289,40 @@ set_http_authn(start) ->
AuthConfig = raw_http_auth_config(), AuthConfig = raw_http_auth_config(),
Set = fun(Protocol) -> Set = fun(Protocol) ->
Chain = emqx_authentication:global_chain(Protocol), Chain = emqx_authentication:global_chain(Protocol),
emqx_authn_test_lib:delete_authenticators([authentication], Chain), emqx_authn_test_lib:delete_authenticators([authentication], Chain),
{ok, _} = emqx:update_config( {ok, _} = emqx:update_config(
?PATH, ?PATH,
{create_authenticator, Chain, AuthConfig}), {create_authenticator, Chain, AuthConfig}
),
{ok, [#{provider := emqx_authn_http}]} = emqx_authentication:list_authenticators(Chain) {ok, [#{provider := emqx_authn_http}]} = emqx_authentication:list_authenticators(Chain)
end, end,
lists:foreach(Set, ?PROTOCOLS), lists:foreach(Set, ?PROTOCOLS),
Handler = fun(Req0, State) -> Handler = fun(Req0, State) ->
ct:pal("Req:~p State:~p~n", [Req0, State]), ct:pal("Req:~p State:~p~n", [Req0, State]),
case cowboy_req:match_qs([username, password], Req0) of case cowboy_req:match_qs([username, password], Req0) of
#{username := <<"admin">>, #{
password := <<"public">>} -> username := <<"admin">>,
Req = cowboy_req:reply(200, Req0); password := <<"public">>
_ -> } ->
Req = cowboy_req:reply(400, Req0) Req = cowboy_req:reply(200, Req0);
end, _ ->
{ok, Req, State} Req = cowboy_req:reply(400, Req0)
end, end,
{ok, Req, State}
end,
emqx_authn_http_test_server:set_handler(Handler); emqx_authn_http_test_server:set_handler(Handler);
set_http_authn(stop) -> set_http_authn(stop) ->
ok = emqx_authn_http_test_server:stop(). ok = emqx_authn_http_test_server:stop().
clear_authn() -> clear_authn() ->
Clear = fun(Protocol) -> Clear = fun(Protocol) ->
Chain = emqx_authentication:global_chain(Protocol), Chain = emqx_authentication:global_chain(Protocol),
emqx_authn_test_lib:delete_authenticators([authentication], Chain) emqx_authn_test_lib:delete_authenticators([authentication], Chain)
end, end,
lists:foreach(Clear, ?PROTOCOLS). lists:foreach(Clear, ?PROTOCOLS).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -242,11 +337,14 @@ test_gateway_with_auths([{Auth, Gateways} | T]) ->
{name, Name} = erlang:fun_info(Auth, name), {name, Name} = erlang:fun_info(Auth, name),
ct:pal("start auth:~p~n", [Name]), ct:pal("start auth:~p~n", [Name]),
Auth(start), Auth(start),
lists:foreach(fun(Gateway) -> lists:foreach(
{name, GwName} = erlang:fun_info(Gateway, name), fun(Gateway) ->
ct:pal("start gateway case:~p~n", [GwName]), {name, GwName} = erlang:fun_info(Gateway, name),
Gateway() ct:pal("start gateway case:~p~n", [GwName]),
end, Gateways), Gateway()
end,
Gateways
),
ct:pal("stop auth:~p~n", [Name]), ct:pal("stop auth:~p~n", [Name]),
Auth(stop), Auth(stop),
test_gateway_with_auths(T); test_gateway_with_auths(T);
@ -263,17 +361,19 @@ merge_conf([Conf | T], Acc) ->
_ -> _ ->
merge_conf(T, Acc) merge_conf(T, Acc)
end; end;
merge_conf([ ], Acc) -> merge_conf([], Acc) ->
erlang:list_to_binary("gateway{" ++ string:join(Acc, ",") ++ "}"). erlang:list_to_binary("gateway{" ++ string:join(Acc, ",") ++ "}").
with_resource(Init, Close, Fun) -> with_resource(Init, Close, Fun) ->
Res = case Init() of Res =
{ok, X} -> X; case Init() of
Other -> Other {ok, X} -> X;
end, Other -> Other
end,
try try
Fun(Res) Fun(Res)
catch C:R:S -> catch
C:R:S ->
Close(Res), Close(Res),
erlang:raise(C, R, S) erlang:raise(C, R, S)
end. end.

View File

@ -61,6 +61,12 @@ end_per_suite(_Cfg) ->
emqx_mgmt_api_test_util:end_suite([emqx_gateway]), emqx_mgmt_api_test_util:end_suite([emqx_gateway]),
ok. ok.
default_config() ->
?CONF_DEFAULT.
stomp_ver() ->
?STOMP_VER.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Test Cases %% Test Cases
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------