From 2bada0bab8dc27b35effa7bd2d51ddfdb33e4ef1 Mon Sep 17 00:00:00 2001 From: Ilya Averyanov Date: Tue, 21 Dec 2021 14:08:13 +0300 Subject: [PATCH] chore(authz): test Mria authz --- apps/emqx_authz/src/emqx_authz_mnesia.erl | 35 +++- .../emqx_authz/test/emqx_authz_file_SUITE.erl | 2 +- .../test/emqx_authz_mnesia_SUITE.erl | 175 +++++++++++------- .../test/emqx_authz_mysql_SUITE.erl | 2 +- .../test/emqx_authz_postgresql_SUITE.erl | 2 +- .../test/emqx_authz_redis_SUITE.erl | 2 +- apps/emqx_authz/test/emqx_authz_test_lib.erl | 1 + 7 files changed, 139 insertions(+), 80 deletions(-) diff --git a/apps/emqx_authz/src/emqx_authz_mnesia.erl b/apps/emqx_authz/src/emqx_authz_mnesia.erl index 2ce8215cd..851fe1522 100644 --- a/apps/emqx_authz/src/emqx_authz_mnesia.erl +++ b/apps/emqx_authz/src/emqx_authz_mnesia.erl @@ -114,18 +114,19 @@ authorize(#{username := Username, %% Management API %%-------------------------------------------------------------------- +-spec(init_tables() -> ok). init_tables() -> ok = mria_rlog:wait_for_shards([?ACL_SHARDED], infinity). -spec(store_rules(who(), rules()) -> ok). store_rules({username, Username}, Rules) -> - Record = #emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = Rules}, + Record = #emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = normalize_rules(Rules)}, mria:dirty_write(Record); store_rules({clientid, Clientid}, Rules) -> - Record = #emqx_acl{who = {?ACL_TABLE_CLIENTID, Clientid}, rules = Rules}, + Record = #emqx_acl{who = {?ACL_TABLE_CLIENTID, Clientid}, rules = normalize_rules(Rules)}, mria:dirty_write(Record); store_rules(all, Rules) -> - Record = #emqx_acl{who = ?ACL_TABLE_ALL, rules = Rules}, + Record = #emqx_acl{who = ?ACL_TABLE_ALL, rules = normalize_rules(Rules)}, mria:dirty_write(Record). -spec(purge_rules() -> ok). @@ -176,6 +177,29 @@ record_count() -> %% Internal functions %%-------------------------------------------------------------------- +normalize_rules(Rules) -> + lists:map(fun normalize_rule/1, Rules). + +normalize_rule({Permission, Action, Topic}) -> + {normalize_permission(Permission), + normalize_action(Action), + normalize_topic(Topic)}; +normalize_rule(Rule) -> + error({invalid_rule, Rule}). + +normalize_topic(Topic) when is_list(Topic) -> list_to_binary(Topic); +normalize_topic(Topic) when is_binary(Topic) -> Topic; +normalize_topic(Topic) -> error({invalid_rule_topic, Topic}). + +normalize_action(publish) -> publish; +normalize_action(subscribe) -> subscribe; +normalize_action(all) -> all; +normalize_action(Action) -> error({invalid_rule_action, Action}). + +normalize_permission(allow) -> allow; +normalize_permission(deny) -> deny; +normalize_permission(Permission) -> error({invalid_rule_permission, Permission}). + do_get_rules(Key) -> case mnesia:dirty_read(?ACL_TABLE, Key) of [#emqx_acl{rules = Rules}] -> {ok, Rules}; @@ -184,9 +208,8 @@ do_get_rules(Key) -> do_authorize(_Client, _PubSub, _Topic, []) -> nomatch; do_authorize(Client, PubSub, Topic, [ {Permission, Action, TopicFilter} | Tail]) -> - case emqx_authz_rule:match(Client, PubSub, Topic, - emqx_authz_rule:compile({Permission, all, Action, [TopicFilter]}) - ) of + Rule = emqx_authz_rule:compile({Permission, all, Action, [TopicFilter]}), + case emqx_authz_rule:match(Client, PubSub, Topic, Rule) of {matched, Permission} -> {matched, Permission}; nomatch -> do_authorize(Client, PubSub, Topic, Tail) end. diff --git a/apps/emqx_authz/test/emqx_authz_file_SUITE.erl b/apps/emqx_authz/test/emqx_authz_file_SUITE.erl index 09c49545c..8424c0939 100644 --- a/apps/emqx_authz/test/emqx_authz_file_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_file_SUITE.erl @@ -38,7 +38,7 @@ end_per_suite(_Config) -> ok = emqx_authz_test_lib:restore_authorizers(), ok = emqx_common_test_helpers:stop_apps([emqx_authz]). -init_per_testcase(Config) -> +init_per_testcase(_TestCase, Config) -> ok = emqx_authz_test_lib:reset_authorizers(), Config. diff --git a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl index dd98f77d3..4d1dce248 100644 --- a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl @@ -18,10 +18,8 @@ -compile(nowarn_export_all). -compile(export_all). --include("emqx_authz.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). --include_lib("emqx/include/emqx_placeholder.hrl"). all() -> emqx_common_test_helpers:all(?MODULE). @@ -31,86 +29,123 @@ groups() -> init_per_suite(Config) -> ok = emqx_common_test_helpers:start_apps( - [emqx_connector, emqx_conf, emqx_authz], - fun set_special_configs/1 - ), + [emqx_conf, emqx_authz], + fun set_special_configs/1), Config. end_per_suite(_Config) -> - {ok, _} = emqx:update_config( - [authorization], - #{<<"no_match">> => <<"allow">>, - <<"cache">> => #{<<"enable">> => <<"true">>}, - <<"sources">> => []}), - emqx_common_test_helpers:stop_apps([emqx_authz, emqx_conf]), - ok. + ok = emqx_authz_test_lib:restore_authorizers(), + ok = emqx_common_test_helpers:stop_apps([emqx_authz]). + +init_per_testcase(_TestCase, Config) -> + ok = emqx_authz_test_lib:reset_authorizers(), + ok = setup_config(), + Config. + +end_per_testcase(_TestCase, _Config) -> + ok = emqx_authz_mnesia:purge_rules(). set_special_configs(emqx_authz) -> - {ok, _} = emqx:update_config([authorization, cache, enable], false), - {ok, _} = emqx:update_config([authorization, no_match], deny), - {ok, _} = emqx:update_config([authorization, sources], - [#{<<"type">> => <<"built-in-database">>}]), - ok; -set_special_configs(_App) -> + ok = emqx_authz_test_lib:reset_authorizers(); + +set_special_configs(_) -> ok. -init_per_testcase(t_authz, Config) -> - emqx_authz_mnesia:store_rules( - {username, <<"test_username">>}, - [{allow, publish, <<"test/", ?PH_S_USERNAME>>}, - {allow, subscribe, <<"eq #">>}]), - - emqx_authz_mnesia:store_rules( - {clientid, <<"test_clientid">>}, - [{allow, publish, <<"test/", ?PH_S_CLIENTID>>}, - {deny, subscribe, <<"eq #">>}]), - - emqx_authz_mnesia:store_rules( - all, - [{deny, all, <<"#">>}]), - - Config; -init_per_testcase(_, Config) -> Config. - -end_per_testcase(t_authz, Config) -> - ok = emqx_authz_mnesia:purge_rules(), - Config; -end_per_testcase(_, Config) -> Config. - %%------------------------------------------------------------------------------ %% Testcases %%------------------------------------------------------------------------------ +t_username_topic_rules(_Config) -> + ok = test_topic_rules(username). -t_authz(_) -> - ClientInfo1 = #{clientid => <<"test">>, - username => <<"test">>, - peerhost => {127,0,0,1}, - listener => {tcp, default} - }, - ClientInfo2 = #{clientid => <<"fake_clientid">>, - username => <<"test_username">>, - peerhost => {127,0,0,1}, - listener => {tcp, default} - }, - ClientInfo3 = #{clientid => <<"test_clientid">>, - username => <<"fake_username">>, - peerhost => {127,0,0,1}, - listener => {tcp, default} - }, +t_clientid_topic_rules(_Config) -> + ok = test_topic_rules(clientid). - ?assertEqual(deny, emqx_access_control:authorize( - ClientInfo1, subscribe, <<"#">>)), - ?assertEqual(deny, emqx_access_control:authorize( - ClientInfo1, publish, <<"#">>)), +t_all_topic_rules(_Config) -> + ok = test_topic_rules(all). - ?assertEqual(allow, emqx_access_control:authorize( - ClientInfo2, publish, <<"test/test_username">>)), - ?assertEqual(allow, emqx_access_control:authorize( - ClientInfo2, subscribe, <<"#">>)), +test_topic_rules(Key) -> + ClientInfo = #{clientid => <<"clientid">>, + username => <<"username">>, + peerhost => {127,0,0,1}, + zone => default, + listener => {tcp, default} + }, - ?assertEqual(allow, emqx_access_control:authorize( - ClientInfo3, publish, <<"test/test_clientid">>)), - ?assertEqual(deny, emqx_access_control:authorize( - ClientInfo3, subscribe, <<"#">>)), + SetupSamples = fun(CInfo, Samples) -> + setup_client_samples(CInfo, Samples, Key) + end, - ok. + ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, SetupSamples), + + ok = emqx_authz_test_lib:test_allow_topic_rules(ClientInfo, SetupSamples), + + ok = emqx_authz_test_lib:test_deny_topic_rules(ClientInfo, SetupSamples). + +t_normalize_rules(_Config) -> + ClientInfo = #{clientid => <<"clientid">>, + username => <<"username">>, + peerhost => {127,0,0,1}, + zone => default, + listener => {tcp, default} + }, + + ok = emqx_authz_mnesia:store_rules( + {username, <<"username">>}, + [{allow, publish, "t"}]), + + ?assertEqual( + allow, + emqx_access_control:authorize(ClientInfo, publish, <<"t">>)), + + ?assertException( + error, + {invalid_rule, _}, + emqx_authz_mnesia:store_rules( + {username, <<"username">>}, + [[allow, publish, <<"t">>]])), + + ?assertException( + error, + {invalid_rule_action, _}, + emqx_authz_mnesia:store_rules( + {username, <<"username">>}, + [{allow, pub, <<"t">>}])), + + ?assertException( + error, + {invalid_rule_permission, _}, + emqx_authz_mnesia:store_rules( + {username, <<"username">>}, + [{accept, publish, <<"t">>}])). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +raw_mnesia_authz_config() -> + #{ + <<"enable">> => <<"true">>, + <<"type">> => <<"built-in-database">> + }. + +setup_client_samples(ClientInfo, Samples, Key) -> + ok = emqx_authz_mnesia:purge_rules(), + Rules = lists:flatmap( + fun(#{topics := Topics, permission := Permission, action := Action}) -> + lists:map( + fun(Topic) -> + {binary_to_atom(Permission), binary_to_atom(Action), Topic} + end, + Topics) + end, + Samples), + #{username := Username, clientid := ClientId} = ClientInfo, + Who = case Key of + username -> {username, Username}; + clientid -> {clientid, ClientId}; + all -> all + end, + ok = emqx_authz_mnesia:store_rules(Who, Rules). + +setup_config() -> + emqx_authz_test_lib:setup_config(raw_mnesia_authz_config(), #{}). diff --git a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl index 7db042dd8..853d78fdf 100644 --- a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl @@ -56,7 +56,7 @@ end_per_suite(_Config) -> ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authz]). -init_per_testcase(Config) -> +init_per_testcase(_TestCase, Config) -> ok = emqx_authz_test_lib:reset_authorizers(), Config. diff --git a/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl index 92c479f92..cda4bb447 100644 --- a/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl @@ -56,7 +56,7 @@ end_per_suite(_Config) -> ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authz]). -init_per_testcase(Config) -> +init_per_testcase(_TestCase, Config) -> ok = emqx_authz_test_lib:reset_authorizers(), Config. diff --git a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl index 93044e044..59d6e653d 100644 --- a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl @@ -57,7 +57,7 @@ end_per_suite(_Config) -> ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authz]). -init_per_testcase(Config) -> +init_per_testcase(_TestCase, Config) -> ok = emqx_authz_test_lib:reset_authorizers(), Config. diff --git a/apps/emqx_authz/test/emqx_authz_test_lib.erl b/apps/emqx_authz/test/emqx_authz_test_lib.erl index 68686837d..9c186ec4d 100644 --- a/apps/emqx_authz/test/emqx_authz_test_lib.erl +++ b/apps/emqx_authz/test/emqx_authz_test_lib.erl @@ -70,6 +70,7 @@ test_samples(ClientInfo, Samples) -> test_no_topic_rules(ClientInfo, SetupSamples) -> %% No rules + ok = reset_authorizers(deny, false), ok = SetupSamples(ClientInfo, []), ok = test_samples(