From 48ddd056b5270b8134b3c5f74e7acb91c97d556b Mon Sep 17 00:00:00 2001 From: zhouzb Date: Thu, 4 Nov 2021 10:03:34 +0800 Subject: [PATCH] test(authn): add test cases for authn --- .../test/emqx_authn_mysql_SUITE.erl | 90 ++++++++++++++++ .../test/emqx_authn_pgsql_SUITE.erl | 101 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl create mode 100644 apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl diff --git a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl new file mode 100644 index 000000000..844f45762 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl @@ -0,0 +1,90 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_authn_mysql_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +groups() -> + []. + +init_per_suite(Config) -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + Config. + +end_per_suite(_Config) -> + emqx_common_test_helpers:stop_apps([emqx_authn]), + ok. + +init_per_testcase(t_authn, Config) -> + meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), + meck:expect(emqx_resource, create_local, fun(_, _, _) -> {ok, undefined} end), + Config; +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(t_authn, _Config) -> + meck:unload(emqx_resource), + ok; +end_per_testcase(_, _Config) -> + ok. + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_authn(_) -> + Password = <<"test">>, + Salt = <<"salt">>, + PasswordHash = emqx_authn_utils:hash(sha256, Password, Salt, prefix), + + Config = #{<<"mechanism">> => <<"password-based">>, + <<"backend">> => <<"mysql">>, + <<"server">> => <<"127.0.0.1:3306">>, + <<"database">> => <<"mqtt">>, + <<"query">> => <<"SELECT password_hash, salt FROM users where username = ${mqtt-username} LIMIT 1">> + }, + {ok, _} = update_config([authentication], {create_authenticator, ?GLOBAL, Config}), + + meck:expect(emqx_resource, query, + fun(_, {sql, _, [<<"good">>], _}) -> + {ok, [<<"password_hash">>, <<"salt">>], [[PasswordHash, Salt]]}; + (_, {sql, _, _, _}) -> + {error, this_is_a_fictitious_reason} + end), + + ClientInfo = #{zone => default, + listener => 'tcp:default', + protocol => mqtt, + username => <<"good">>, + password => Password}, + ?assertEqual({ok, #{is_superuser => false}}, emqx_access_control:authenticate(ClientInfo)), + + ClientInfo2 = ClientInfo#{username => <<"bad">>}, + ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(ClientInfo2)), + + ?AUTHN:delete_chain(?GLOBAL). + +update_config(Path, ConfigRequest) -> + emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}). + diff --git a/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl new file mode 100644 index 000000000..574a11c74 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl @@ -0,0 +1,101 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_authn_pgsql_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("epgsql/include/epgsql.hrl"). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +groups() -> + []. + +init_per_suite(Config) -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + Config. + +end_per_suite(_Config) -> + emqx_common_test_helpers:stop_apps([emqx_authn]), + ok. + +init_per_testcase(t_authn, Config) -> + meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), + meck:expect(emqx_resource, create_local, fun(_, _, _) -> {ok, undefined} end), + Config; +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(t_authn, _Config) -> + meck:unload(emqx_resource), + ok; +end_per_testcase(_, _Config) -> + ok. + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_parse_query(_) -> + Query1 = <<"${mqtt-username}">>, + ?assertEqual({<<"$1">>, [<<"${mqtt-username}">>]}, emqx_authn_pgsql:parse_query(Query1)), + + Query2 = <<"${mqtt-username}, ${mqtt-clientid}">>, + ?assertEqual({<<"$1, $2">>, [<<"${mqtt-username}">>, <<"${mqtt-clientid}">>]}, emqx_authn_pgsql:parse_query(Query2)), + + Query3 = <<"nomatch">>, + ?assertEqual({<<"nomatch">>, []}, emqx_authn_pgsql:parse_query(Query3)). + +t_authn(_) -> + Password = <<"test">>, + Salt = <<"salt">>, + PasswordHash = emqx_authn_utils:hash(sha256, Password, Salt, prefix), + + Config = #{<<"mechanism">> => <<"password-based">>, + <<"backend">> => <<"postgresql">>, + <<"server">> => <<"127.0.0.1:5432">>, + <<"database">> => <<"mqtt">>, + <<"query">> => <<"SELECT password_hash, salt FROM users where username = ${mqtt-username} LIMIT 1">> + }, + {ok, _} = update_config([authentication], {create_authenticator, ?GLOBAL, Config}), + + meck:expect(emqx_resource, query, + fun(_, {sql, _, [<<"good">>]}) -> + {ok, [#column{name = <<"password_hash">>}, #column{name = <<"salt">>}], [{PasswordHash, Salt}]}; + (_, {sql, _, _}) -> + {error, this_is_a_fictitious_reason} + end), + + ClientInfo = #{zone => default, + listener => 'tcp:default', + protocol => mqtt, + username => <<"good">>, + password => Password}, + ?assertEqual({ok, #{is_superuser => false}}, emqx_access_control:authenticate(ClientInfo)), + + ClientInfo2 = ClientInfo#{username => <<"bad">>}, + ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(ClientInfo2)), + + ?AUTHN:delete_chain(?GLOBAL). + +update_config(Path, ConfigRequest) -> + emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}). +