Merge pull request #11743 from savonarola/1010-auth-leftovers

chore(auth): cleanup code
This commit is contained in:
Ilya Averyanov 2023-10-11 15:09:07 +03:00 committed by GitHub
commit 57655854d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 208 additions and 110 deletions

View File

@ -163,3 +163,8 @@
-define(DEFAULT_RULE_QOS, [0, 1, 2]). -define(DEFAULT_RULE_QOS, [0, 1, 2]).
-define(DEFAULT_RULE_RETAIN, all). -define(DEFAULT_RULE_RETAIN, all).
-define(BUILTIN_SOURCES, [
{client_info, emqx_authz_client_info},
{file, emqx_authz_file}
]).

View File

@ -26,6 +26,7 @@
-include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/logger.hrl").
-include_lib("emqx/include/emqx_hooks.hrl"). -include_lib("emqx/include/emqx_hooks.hrl").
-include_lib("stdlib/include/ms_transform.hrl"). -include_lib("stdlib/include/ms_transform.hrl").
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
-define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM). -define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
@ -446,7 +447,7 @@ handle_continue(initialize_authentication, #{init_done := true} = State) ->
{noreply, State}; {noreply, State};
handle_continue(initialize_authentication, #{providers := Providers} = State) -> handle_continue(initialize_authentication, #{providers := Providers} = State) ->
InitDone = initialize_authentication(Providers), InitDone = initialize_authentication(Providers),
{noreply, State#{init_done := InitDone}}. {noreply, maybe_hook(State#{init_done := InitDone})}.
handle_cast(Req, State) -> handle_cast(Req, State) ->
?SLOG(error, #{msg => "unexpected_cast", cast => Req}), ?SLOG(error, #{msg => "unexpected_cast", cast => Req}),
@ -495,6 +496,7 @@ do_initialize_authentication(Providers, Chains, _HasProviders = true) ->
Chains Chains
), ),
ok = unhook_deny(), ok = unhook_deny(),
?tp(info, authn_chains_initialization_done, #{}),
true. true.
initialize_chain_authentication(_Providers, _ChainName, []) -> initialize_chain_authentication(_Providers, _ChainName, []) ->

View File

@ -88,8 +88,7 @@ init() ->
emqx_conf:add_handler(?CONF_KEY_PATH, ?MODULE), emqx_conf:add_handler(?CONF_KEY_PATH, ?MODULE),
emqx_conf:add_handler(?ROOT_KEY, ?MODULE), emqx_conf:add_handler(?ROOT_KEY, ?MODULE),
ok = emqx_hooks:put('client.authorize', {?MODULE, authorize_deny, []}, ?HP_AUTHZ), ok = emqx_hooks:put('client.authorize', {?MODULE, authorize_deny, []}, ?HP_AUTHZ),
ok = register_source(client_info, emqx_authz_client_info), ok = register_builtin_sources(),
ok = register_source(file, emqx_authz_file),
ok. ok.
register_source(Type, Module) -> register_source(Type, Module) ->
@ -124,6 +123,14 @@ are_all_providers_registered() ->
false false
end. end.
register_builtin_sources() ->
lists:foreach(
fun({Type, Module}) ->
register_source(Type, Module)
end,
?BUILTIN_SOURCES
).
configured_types() -> configured_types() ->
lists:map( lists:map(
fun(#{type := Type}) -> Type end, fun(#{type := Type}) -> Type end,
@ -186,8 +193,14 @@ pre_config_update(Path, Cmd, Sources) ->
{error, Reason} -> {error, Reason}; {error, Reason} -> {error, Reason};
NSources -> {ok, NSources} NSources -> {ok, NSources}
catch catch
Error:Reason:Stack -> throw:Reason ->
?SLOG(info, #{ ?SLOG(info, #{
msg => "error_in_pre_config_update",
reason => Reason
}),
{error, Reason};
Error:Reason:Stack ->
?SLOG(warning, #{
msg => "error_in_pre_config_update", msg => "error_in_pre_config_update",
exception => Error, exception => Error,
reason => Reason, reason => Reason,
@ -572,10 +585,6 @@ maybe_convert_sources(
maybe_convert_sources(RawConf, _Fun) -> maybe_convert_sources(RawConf, _Fun) ->
RawConf. RawConf.
% read_acl_file(#{<<"path">> := Path} = Source) ->
% {ok, Rules} = emqx_authz_file:read_file(Path),
% maps:remove(<<"path">>, Source#{<<"rules">> => Rules}).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Extended Features %% Extended Features
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -682,7 +691,7 @@ maybe_read_source_files_safe(Source0) ->
catch catch
Error:Reason:Stacktrace -> Error:Reason:Stacktrace ->
?SLOG(error, #{ ?SLOG(error, #{
msg => "error_in_maybe_read_source_files", msg => "error_when_reading_source_files",
exception => Error, exception => Error,
reason => Reason, reason => Reason,
stacktrace => Stacktrace stacktrace => Stacktrace

View File

@ -19,6 +19,7 @@
-compile(export_all). -compile(export_all).
-compile(nowarn_export_all). -compile(nowarn_export_all).
-include_lib("emqx/include/asserts.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
@ -48,9 +49,11 @@ init_per_testcase(_Case, Config) ->
work_dir => ?config(priv_dir, Config) work_dir => ?config(priv_dir, Config)
} }
), ),
ok = snabbkaffe:start_trace(),
[{apps, Apps} | Config]. [{apps, Apps} | Config].
end_per_testcase(_Case, Config) -> end_per_testcase(_Case, Config) ->
ok = snabbkaffe:stop(),
_ = application:stop(emqx_auth), _ = application:stop(emqx_auth),
ok = emqx_cth_suite:stop(?config(apps, Config)), ok = emqx_cth_suite:stop(?config(apps, Config)),
ok. ok.
@ -66,10 +69,14 @@ t_initialize(_Config) ->
emqx_access_control:authenticate(?CLIENTINFO) emqx_access_control:authenticate(?CLIENTINFO)
), ),
ok = emqx_authn_test_lib:register_fake_providers([{password_based, built_in_database}]), ?assertWaitEvent(
ok = emqx_authn_test_lib:register_fake_providers([{password_based, built_in_database}]),
#{?snk_kind := authn_chains_initialization_done},
100
),
?assertMatch( ?assertMatch(
{error, not_authorized}, {error, bad_username_or_password},
emqx_access_control:authenticate(?CLIENTINFO) emqx_access_control:authenticate(?CLIENTINFO)
), ),

View File

@ -20,7 +20,6 @@
-include("emqx_authz.hrl"). -include("emqx_authz.hrl").
-include_lib("emqx/include/emqx.hrl"). -include_lib("emqx/include/emqx.hrl").
-include_lib("emqx/include/emqx_mqtt.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-include_lib("emqx/include/emqx_placeholder.hrl"). -include_lib("emqx/include/emqx_placeholder.hrl").
@ -35,33 +34,18 @@ groups() ->
[]. [].
init_per_suite(Config) -> init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create_local, fun(_, _, _, _) -> {ok, meck_data} end),
meck:expect(emqx_resource, remove_local, fun(_) -> ok end),
meck:expect(
emqx_authz_file,
acl_conf_file,
fun() ->
emqx_common_test_helpers:deps_path(emqx_auth, "etc/acl.conf")
end
),
Apps = emqx_cth_suite:start( Apps = emqx_cth_suite:start(
[ [
emqx, emqx,
{emqx_conf, {emqx_conf,
"authorization { cache { enable = false }, no_match = deny, sources = [] }"}, "authorization { cache { enable = false }, no_match = deny, sources = [] }"},
emqx_auth, emqx_auth
emqx_auth_http,
emqx_auth_mnesia,
emqx_auth_redis,
emqx_auth_postgresql,
emqx_auth_mysql,
emqx_auth_mongodb
], ],
#{ #{
work_dir => filename:join(?config(priv_dir, Config), ?MODULE) work_dir => filename:join(?config(priv_dir, Config), ?MODULE)
} }
), ),
ok = emqx_authz_test_lib:register_fake_sources([http, redis, mongodb, mysql, postgresql]),
[{suite_apps, Apps} | Config]. [{suite_apps, Apps} | Config].
end_per_suite(Config) -> end_per_suite(Config) ->
@ -73,8 +57,8 @@ end_per_suite(Config) ->
<<"sources">> => [] <<"sources">> => []
} }
), ),
ok = emqx_authz_test_lib:deregister_sources(),
emqx_cth_suite:stop(?config(suite_apps, Config)), emqx_cth_suite:stop(?config(suite_apps, Config)),
meck:unload(emqx_resource),
ok. ok.
init_per_testcase(TestCase, Config) when init_per_testcase(TestCase, Config) when
@ -102,7 +86,7 @@ end_per_testcase(_TestCase, _Config) ->
emqx_common_test_helpers:call_janitor(), emqx_common_test_helpers:call_janitor(),
ok. ok.
-define(SOURCE1, #{ -define(SOURCE_HTTP, #{
<<"type">> => <<"http">>, <<"type">> => <<"http">>,
<<"enable">> => true, <<"enable">> => true,
<<"url">> => <<"https://example.com:443/a/b?c=d">>, <<"url">> => <<"https://example.com:443/a/b?c=d">>,
@ -111,7 +95,7 @@ end_per_testcase(_TestCase, _Config) ->
<<"method">> => <<"get">>, <<"method">> => <<"get">>,
<<"request_timeout">> => <<"5s">> <<"request_timeout">> => <<"5s">>
}). }).
-define(SOURCE2, #{ -define(SOURCE_MONGODB, #{
<<"type">> => <<"mongodb">>, <<"type">> => <<"mongodb">>,
<<"enable">> => true, <<"enable">> => true,
<<"mongo_type">> => <<"single">>, <<"mongo_type">> => <<"single">>,
@ -123,7 +107,7 @@ end_per_testcase(_TestCase, _Config) ->
<<"collection">> => <<"authz">>, <<"collection">> => <<"authz">>,
<<"filter">> => #{<<"a">> => <<"b">>} <<"filter">> => #{<<"a">> => <<"b">>}
}). }).
-define(SOURCE3, #{ -define(SOURCE_MYSQL, #{
<<"type">> => <<"mysql">>, <<"type">> => <<"mysql">>,
<<"enable">> => true, <<"enable">> => true,
<<"server">> => <<"127.0.0.1:27017">>, <<"server">> => <<"127.0.0.1:27017">>,
@ -135,7 +119,7 @@ end_per_testcase(_TestCase, _Config) ->
<<"ssl">> => #{<<"enable">> => false}, <<"ssl">> => #{<<"enable">> => false},
<<"query">> => <<"abcb">> <<"query">> => <<"abcb">>
}). }).
-define(SOURCE4, #{ -define(SOURCE_POSTGRESQL, #{
<<"type">> => <<"postgresql">>, <<"type">> => <<"postgresql">>,
<<"enable">> => true, <<"enable">> => true,
<<"server">> => <<"127.0.0.1:27017">>, <<"server">> => <<"127.0.0.1:27017">>,
@ -147,7 +131,7 @@ end_per_testcase(_TestCase, _Config) ->
<<"ssl">> => #{<<"enable">> => false}, <<"ssl">> => #{<<"enable">> => false},
<<"query">> => <<"abcb">> <<"query">> => <<"abcb">>
}). }).
-define(SOURCE5, #{ -define(SOURCE_REDIS, #{
<<"type">> => <<"redis">>, <<"type">> => <<"redis">>,
<<"redis_type">> => <<"single">>, <<"redis_type">> => <<"single">>,
<<"enable">> => true, <<"enable">> => true,
@ -160,22 +144,22 @@ end_per_testcase(_TestCase, _Config) ->
<<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>> <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
}). }).
-define(FILE_SOURCE(Rules), #{ -define(SOURCE_FILE(Rules), #{
<<"type">> => <<"file">>, <<"type">> => <<"file">>,
<<"enable">> => true, <<"enable">> => true,
<<"rules">> => Rules <<"rules">> => Rules
}). }).
-define(SOURCE6, -define(SOURCE_FILE1,
?FILE_SOURCE( ?SOURCE_FILE(
<< <<
"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}." "{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
"\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}." "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}."
>> >>
) )
). ).
-define(SOURCE7, -define(SOURCE_FILE2,
?FILE_SOURCE( ?SOURCE_FILE(
<< <<
"{allow,{username,\"some_client\"},publish,[\"some_client/lwt\"]}.\n" "{allow,{username,\"some_client\"},publish,[\"some_client/lwt\"]}.\n"
"{deny, all}." "{deny, all}."
@ -183,15 +167,6 @@ end_per_testcase(_TestCase, _Config) ->
) )
). ).
-define(BAD_FILE_SOURCE2, #{
<<"type">> => <<"file">>,
<<"enable">> => true,
<<"rules">> =>
<<
"{not_allow,{username,\"some_client\"},publish,[\"some_client/lwt\"]}."
>>
}).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Testcases %% Testcases
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -199,24 +174,23 @@ end_per_testcase(_TestCase, _Config) ->
-define(UPDATE_ERROR(Err), {error, {pre_config_update, emqx_authz, Err}}). -define(UPDATE_ERROR(Err), {error, {pre_config_update, emqx_authz, Err}}).
t_bad_file_source(_) -> t_bad_file_source(_) ->
BadContent = ?FILE_SOURCE(<<"{allow,{username,\"bar\"}, publish, [\"test\"]}">>), BadContent = ?SOURCE_FILE(<<"{allow,{username,\"bar\"}, publish, [\"test\"]}">>),
BadContentErr = {bad_acl_file_content, {1, erl_parse, ["syntax error before: ", []]}}, BadContentErr = {bad_acl_file_content, {1, erl_parse, ["syntax error before: ", []]}},
BadRule = ?FILE_SOURCE(<<"{allow,{username,\"bar\"},publish}.">>), BadRule = ?SOURCE_FILE(<<"{allow,{username,\"bar\"},publish}.">>),
BadRuleErr = {invalid_authorization_rule, {allow, {username, "bar"}, publish}}, BadRuleErr = {invalid_authorization_rule, {allow, {username, "bar"}, publish}},
BadPermission = ?FILE_SOURCE(<<"{not_allow,{username,\"bar\"},publish,[\"test\"]}.">>), BadPermission = ?SOURCE_FILE(<<"{not_allow,{username,\"bar\"},publish,[\"test\"]}.">>),
BadPermissionErr = {invalid_authorization_permission, not_allow}, BadPermissionErr = {invalid_authorization_permission, not_allow},
BadAction = ?FILE_SOURCE(<<"{allow,{username,\"bar\"},pubsub,[\"test\"]}.">>), BadAction = ?SOURCE_FILE(<<"{allow,{username,\"bar\"},pubsub,[\"test\"]}.">>),
BadActionErr = {invalid_authorization_action, pubsub}, BadActionErr = {invalid_authorization_action, pubsub},
lists:foreach( lists:foreach(
fun({Source, Error}) -> fun({Source, Error}) ->
File = emqx_authz_file:acl_conf_file(), File = emqx_authz_file:acl_conf_file(),
{ok, Bin1} = file:read_file(File), ?assertEqual({error, enoent}, file:read_file(File)),
?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_REPLACE, [Source])), ?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_REPLACE, [Source])),
?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_PREPEND, Source)), ?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_PREPEND, Source)),
?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_APPEND, Source)), ?assertEqual(?UPDATE_ERROR(Error), emqx_authz:update(?CMD_APPEND, Source)),
%% Check file content not changed if update failed %% Check file is not created if update failed;
{ok, Bin2} = file:read_file(File), ?assertEqual({error, enoent}, file:read_file(File))
?assertEqual(Bin1, Bin2)
end, end,
[ [
{BadContent, BadContentErr}, {BadContent, BadContentErr},
@ -230,14 +204,32 @@ t_bad_file_source(_) ->
emqx_conf:get([authorization, sources], []) emqx_conf:get([authorization, sources], [])
). ).
t_good_file_source(_) ->
RuleBin = <<"{allow,{username,\"bar\"}, publish, [\"test\"]}.">>,
GoodFileSource = ?SOURCE_FILE(RuleBin),
File = emqx_authz_file:acl_conf_file(),
lists:foreach(
fun({Command, Argument}) ->
_ = file:delete(File),
?assertMatch({ok, _}, emqx_authz:update(Command, Argument)),
?assertEqual({ok, RuleBin}, file:read_file(File)),
{ok, _} = emqx_authz:update(?CMD_REPLACE, [])
end,
[
{?CMD_REPLACE, [GoodFileSource]},
{?CMD_PREPEND, GoodFileSource},
{?CMD_APPEND, GoodFileSource}
]
).
t_update_source(_) -> t_update_source(_) ->
%% replace all %% replace all
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE3]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE_MYSQL]),
{ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE2), {ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE_MONGODB),
{ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE1), {ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE_HTTP),
{ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE4), {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE_POSTGRESQL),
{ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE5), {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE_REDIS),
{ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE6), {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE_FILE1),
?assertMatch( ?assertMatch(
[ [
@ -251,19 +243,23 @@ t_update_source(_) ->
emqx_conf:get([authorization, sources], []) emqx_conf:get([authorization, sources], [])
), ),
{ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := true}), {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE_HTTP#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE2#{<<"enable">> := true}), {ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE_MONGODB#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE3#{<<"enable">> := true}), {ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE_MYSQL#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE4#{<<"enable">> := true}), {ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE_POSTGRESQL#{
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE5#{<<"enable">> := true}), <<"enable">> := true
{ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := true}), }),
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE_REDIS#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE_FILE1#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := false}), {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE_HTTP#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE2#{<<"enable">> := false}), {ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE_MONGODB#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE3#{<<"enable">> := false}), {ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE_MYSQL#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE4#{<<"enable">> := false}), {ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE_POSTGRESQL#{
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE5#{<<"enable">> := false}), <<"enable">> := false
{ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := false}), }),
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE_REDIS#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE_FILE1#{<<"enable">> := false}),
?assertMatch( ?assertMatch(
[ [
@ -295,7 +291,12 @@ t_replace_all(_) ->
Conf = emqx:get_raw_config(RootKey), Conf = emqx:get_raw_config(RootKey),
emqx_authz_utils:update_config(RootKey, Conf#{ emqx_authz_utils:update_config(RootKey, Conf#{
<<"sources">> => [ <<"sources">> => [
?SOURCE6, ?SOURCE5, ?SOURCE4, ?SOURCE3, ?SOURCE2, ?SOURCE1 ?SOURCE_FILE1,
?SOURCE_REDIS,
?SOURCE_POSTGRESQL,
?SOURCE_MYSQL,
?SOURCE_MONGODB,
?SOURCE_HTTP
] ]
}), }),
%% config %% config
@ -335,7 +336,7 @@ t_replace_all(_) ->
{ok, _}, {ok, _},
emqx_authz_utils:update_config( emqx_authz_utils:update_config(
RootKey, RootKey,
Conf#{<<"sources">> => [?SOURCE1#{<<"enable">> => false}]} Conf#{<<"sources">> => [?SOURCE_HTTP#{<<"enable">> => false}]}
) )
), ),
%% hooks status %% hooks status
@ -351,7 +352,7 @@ t_replace_all(_) ->
ok. ok.
t_delete_source(_) -> t_delete_source(_) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE1]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE_HTTP]),
?assertMatch([#{type := http, enable := true}], emqx_conf:get([authorization, sources], [])), ?assertMatch([#{type := http, enable := true}], emqx_conf:get([authorization, sources], [])),
@ -363,12 +364,12 @@ t_move_source(_) ->
{ok, _} = emqx_authz:update( {ok, _} = emqx_authz:update(
?CMD_REPLACE, ?CMD_REPLACE,
[ [
?SOURCE1, ?SOURCE_HTTP,
?SOURCE2, ?SOURCE_MONGODB,
?SOURCE3, ?SOURCE_MYSQL,
?SOURCE4, ?SOURCE_POSTGRESQL,
?SOURCE5, ?SOURCE_REDIS,
?SOURCE6 ?SOURCE_FILE1
] ]
), ),
?assertMatch( ?assertMatch(
@ -437,15 +438,26 @@ t_move_source(_) ->
ok. ok.
t_pre_config_update_crash(_) ->
ok = meck:new(emqx_authz_fake_source, [non_strict, passthrough, no_history]),
ok = meck:expect(emqx_authz_fake_source, write_files, fun(_) -> meck:exception(error, oops) end),
?assertEqual(
{error, {pre_config_update, emqx_authz, oops}},
emqx_authz:update(?CMD_APPEND, ?SOURCE_HTTP)
),
ok = meck:unload(emqx_authz_fake_source).
t_get_enabled_authzs_none_enabled(_Config) -> t_get_enabled_authzs_none_enabled(_Config) ->
?assertEqual([], emqx_authz:get_enabled_authzs()). ?assertEqual([], emqx_authz:get_enabled_authzs()).
t_get_enabled_authzs_some_enabled(_Config) -> t_get_enabled_authzs_some_enabled(_Config) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE4, ?SOURCE5#{<<"enable">> := false}]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [
?SOURCE_POSTGRESQL, ?SOURCE_REDIS#{<<"enable">> := false}
]),
?assertEqual([postgresql], emqx_authz:get_enabled_authzs()). ?assertEqual([postgresql], emqx_authz:get_enabled_authzs()).
t_subscribe_deny_disconnect_publishes_last_will_testament(_Config) -> t_subscribe_deny_disconnect_publishes_last_will_testament(_Config) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE7]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE_FILE2]),
{ok, C} = emqtt:start_link([ {ok, C} = emqtt:start_link([
{username, <<"some_client">>}, {username, <<"some_client">>},
{will_topic, <<"some_client/lwt">>}, {will_topic, <<"some_client/lwt">>},
@ -473,7 +485,7 @@ t_subscribe_deny_disconnect_publishes_last_will_testament(_Config) ->
ok. ok.
t_publish_deny_disconnect_publishes_last_will_testament(_Config) -> t_publish_deny_disconnect_publishes_last_will_testament(_Config) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE7]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE_FILE2]),
{ok, C} = emqtt:start_link([ {ok, C} = emqtt:start_link([
{username, <<"some_client">>}, {username, <<"some_client">>},
{will_topic, <<"some_client/lwt">>}, {will_topic, <<"some_client/lwt">>},
@ -530,7 +542,7 @@ t_publish_last_will_testament_denied_topic(_Config) ->
%% and then gets banned and kicked out while connected. Should not %% and then gets banned and kicked out while connected. Should not
%% publish LWT. %% publish LWT.
t_publish_last_will_testament_banned_client_connecting(_Config) -> t_publish_last_will_testament_banned_client_connecting(_Config) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE7]), {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE_FILE2]),
Username = <<"some_client">>, Username = <<"some_client">>,
ClientId = <<"some_clientid">>, ClientId = <<"some_clientid">>,
LWTPayload = <<"should not be published">>, LWTPayload = <<"should not be published">>,

View File

@ -29,7 +29,7 @@
-define(PGSQL_HOST, "pgsql"). -define(PGSQL_HOST, "pgsql").
-define(REDIS_SINGLE_HOST, "redis"). -define(REDIS_SINGLE_HOST, "redis").
-define(SOURCE1, #{ -define(SOURCE_REDIS1, #{
<<"type">> => <<"http">>, <<"type">> => <<"http">>,
<<"enable">> => true, <<"enable">> => true,
<<"url">> => <<"https://fake.com:443/acl?username=", ?PH_USERNAME/binary>>, <<"url">> => <<"https://fake.com:443/acl?username=", ?PH_USERNAME/binary>>,
@ -38,7 +38,7 @@
<<"method">> => <<"get">>, <<"method">> => <<"get">>,
<<"request_timeout">> => <<"5s">> <<"request_timeout">> => <<"5s">>
}). }).
-define(SOURCE2, #{ -define(SOURCE_MONGODB, #{
<<"type">> => <<"mongodb">>, <<"type">> => <<"mongodb">>,
<<"enable">> => true, <<"enable">> => true,
<<"mongo_type">> => <<"single">>, <<"mongo_type">> => <<"single">>,
@ -50,7 +50,7 @@
<<"collection">> => <<"fake">>, <<"collection">> => <<"fake">>,
<<"filter">> => #{<<"a">> => <<"b">>} <<"filter">> => #{<<"a">> => <<"b">>}
}). }).
-define(SOURCE3, #{ -define(SOURCE_MYSQL, #{
<<"type">> => <<"mysql">>, <<"type">> => <<"mysql">>,
<<"enable">> => true, <<"enable">> => true,
<<"server">> => <<?MYSQL_HOST>>, <<"server">> => <<?MYSQL_HOST>>,
@ -62,7 +62,7 @@
<<"ssl">> => #{<<"enable">> => false}, <<"ssl">> => #{<<"enable">> => false},
<<"query">> => <<"abcb">> <<"query">> => <<"abcb">>
}). }).
-define(SOURCE4, #{ -define(SOURCE_POSTGRESQL, #{
<<"type">> => <<"postgresql">>, <<"type">> => <<"postgresql">>,
<<"enable">> => true, <<"enable">> => true,
<<"server">> => <<?PGSQL_HOST>>, <<"server">> => <<?PGSQL_HOST>>,
@ -74,7 +74,7 @@
<<"ssl">> => #{<<"enable">> => false}, <<"ssl">> => #{<<"enable">> => false},
<<"query">> => <<"abcb">> <<"query">> => <<"abcb">>
}). }).
-define(SOURCE5, #{ -define(SOURCE_REDIS2, #{
<<"type">> => <<"redis">>, <<"type">> => <<"redis">>,
<<"enable">> => true, <<"enable">> => true,
<<"servers">> => <<?REDIS_SINGLE_HOST, ",127.0.0.1:6380">>, <<"servers">> => <<?REDIS_SINGLE_HOST, ",127.0.0.1:6380">>,
@ -85,7 +85,7 @@
<<"ssl">> => #{<<"enable">> => false}, <<"ssl">> => #{<<"enable">> => false},
<<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>> <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
}). }).
-define(SOURCE6, #{ -define(SOURCE_FILE, #{
<<"type">> => <<"file">>, <<"type">> => <<"file">>,
<<"enable">> => true, <<"enable">> => true,
<<"rules">> => <<"rules">> =>
@ -120,12 +120,6 @@ init_per_suite(Config) ->
{emqx_conf, {emqx_conf,
"authorization { cache { enable = false }, no_match = deny, sources = [] }"}, "authorization { cache { enable = false }, no_match = deny, sources = [] }"},
emqx_auth, emqx_auth,
emqx_auth_http,
emqx_auth_mnesia,
emqx_auth_redis,
emqx_auth_postgresql,
emqx_auth_mysql,
emqx_auth_mongodb,
emqx_management, emqx_management,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"} {emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
], ],
@ -133,6 +127,7 @@ init_per_suite(Config) ->
work_dir => filename:join(?config(priv_dir, Config), ?MODULE) work_dir => filename:join(?config(priv_dir, Config), ?MODULE)
} }
), ),
ok = emqx_authz_test_lib:register_fake_sources([http, mongodb, mysql, postgresql, redis]),
_ = emqx_common_test_http:create_default_app(), _ = emqx_common_test_http:create_default_app(),
[{suite_apps, Apps} | Config]. [{suite_apps, Apps} | Config].
@ -192,9 +187,11 @@ t_api(_) ->
begin begin
{ok, 204, _} = request(post, uri(["authorization", "sources"]), Source) {ok, 204, _} = request(post, uri(["authorization", "sources"]), Source)
end end
|| Source <- lists:reverse([?SOURCE2, ?SOURCE3, ?SOURCE4, ?SOURCE5, ?SOURCE6]) || Source <- lists:reverse([
?SOURCE_MONGODB, ?SOURCE_MYSQL, ?SOURCE_POSTGRESQL, ?SOURCE_REDIS2, ?SOURCE_FILE
])
], ],
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE1), {ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE_REDIS1),
{ok, 200, Result2} = request(get, uri(["authorization", "sources"]), []), {ok, 200, Result2} = request(get, uri(["authorization", "sources"]), []),
Sources = get_sources(Result2), Sources = get_sources(Result2),
@ -214,7 +211,7 @@ t_api(_) ->
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "http"]), uri(["authorization", "sources", "http"]),
?SOURCE1#{<<"enable">> := false} ?SOURCE_REDIS1#{<<"enable">> := false}
), ),
{ok, 200, Result3} = request(get, uri(["authorization", "sources", "http"]), []), {ok, 200, Result3} = request(get, uri(["authorization", "sources", "http"]), []),
?assertMatch( ?assertMatch(
@ -238,7 +235,7 @@ t_api(_) ->
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "mongodb"]), uri(["authorization", "sources", "mongodb"]),
?SOURCE2#{ ?SOURCE_MONGODB#{
<<"ssl">> => #{ <<"ssl">> => #{
<<"enable">> => <<"true">>, <<"enable">> => <<"true">>,
<<"cacertfile">> => Cacertfile, <<"cacertfile">> => Cacertfile,
@ -279,7 +276,7 @@ t_api(_) ->
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "mongodb"]), uri(["authorization", "sources", "mongodb"]),
?SOURCE2#{ ?SOURCE_MONGODB#{
<<"ssl">> => #{ <<"ssl">> => #{
<<"enable">> => <<"true">>, <<"enable">> => <<"true">>,
<<"cacertfile">> => Cacert, <<"cacertfile">> => Cacert,
@ -329,19 +326,19 @@ t_api(_) ->
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "mysql"]), uri(["authorization", "sources", "mysql"]),
?SOURCE3#{<<"server">> := <<"192.168.1.100:3306">>} ?SOURCE_MYSQL#{<<"server">> := <<"192.168.1.100:3306">>}
), ),
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "postgresql"]), uri(["authorization", "sources", "postgresql"]),
?SOURCE4#{<<"server">> := <<"fake">>} ?SOURCE_POSTGRESQL#{<<"server">> := <<"fake">>}
), ),
{ok, 204, _} = request( {ok, 204, _} = request(
put, put,
uri(["authorization", "sources", "redis"]), uri(["authorization", "sources", "redis"]),
?SOURCE5#{ ?SOURCE_REDIS2#{
<<"servers">> := [ <<"servers">> := [
<<"192.168.1.100:6379">>, <<"192.168.1.100:6379">>,
<<"192.168.1.100:6380">> <<"192.168.1.100:6380">>
@ -413,7 +410,7 @@ t_api(_) ->
#{<<"type">> => <<"built_in_database">>, <<"enable">> => false} #{<<"type">> => <<"built_in_database">>, <<"enable">> => false}
), ),
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE6), {ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE_FILE),
{ok, Client} = emqtt:start_link( {ok, Client} = emqtt:start_link(
[ [
@ -505,7 +502,9 @@ t_api(_) ->
ok. ok.
t_source_move(_) -> t_source_move(_) ->
{ok, _} = emqx_authz:update(replace, [?SOURCE1, ?SOURCE2, ?SOURCE3, ?SOURCE4, ?SOURCE5]), {ok, _} = emqx_authz:update(replace, [
?SOURCE_REDIS1, ?SOURCE_MONGODB, ?SOURCE_MYSQL, ?SOURCE_POSTGRESQL, ?SOURCE_REDIS2
]),
?assertMatch( ?assertMatch(
[ [
#{type := http}, #{type := http},

View File

@ -0,0 +1,46 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2022-2023 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_authz_fake_source).
-behaviour(emqx_authz_source).
%% APIs
-export([
description/0,
create/1,
update/1,
destroy/1,
authorize/4
]).
%%--------------------------------------------------------------------
%% emqx_authz callbacks
%%--------------------------------------------------------------------
description() ->
"Fake AuthZ".
create(Source) ->
Source.
update(Source) ->
Source.
destroy(_Source) -> ok.
authorize(_Client, _PubSub, _Topic, _Source) ->
nomatch.

View File

@ -51,6 +51,24 @@ setup_config(BaseConfig, SpecialParams) ->
{error, Reason} -> {error, Reason} {error, Reason} -> {error, Reason}
end. end.
register_fake_sources(SourceTypes) ->
lists:foreach(
fun(Type) ->
emqx_authz_source_registry:register(Type, emqx_authz_fake_source)
end,
SourceTypes
).
deregister_sources() ->
{BuiltInTypes, _} = lists:unzip(?BUILTIN_SOURCES),
SourceTypes = emqx_authz_source_registry:get(),
lists:foreach(
fun(Type) ->
emqx_authz_source_registry:register(Type, emqx_authz_fake_source)
end,
SourceTypes -- BuiltInTypes
).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Table-based test helpers %% Table-based test helpers
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------