%%-------------------------------------------------------------------- %% 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}).