chore(authz): test Mria authz
This commit is contained in:
parent
d75e0104cc
commit
2bada0bab8
|
@ -114,18 +114,19 @@ authorize(#{username := Username,
|
||||||
%% Management API
|
%% Management API
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec(init_tables() -> ok).
|
||||||
init_tables() ->
|
init_tables() ->
|
||||||
ok = mria_rlog:wait_for_shards([?ACL_SHARDED], infinity).
|
ok = mria_rlog:wait_for_shards([?ACL_SHARDED], infinity).
|
||||||
|
|
||||||
-spec(store_rules(who(), rules()) -> ok).
|
-spec(store_rules(who(), rules()) -> ok).
|
||||||
store_rules({username, Username}, Rules) ->
|
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);
|
mria:dirty_write(Record);
|
||||||
store_rules({clientid, Clientid}, Rules) ->
|
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);
|
mria:dirty_write(Record);
|
||||||
store_rules(all, Rules) ->
|
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).
|
mria:dirty_write(Record).
|
||||||
|
|
||||||
-spec(purge_rules() -> ok).
|
-spec(purge_rules() -> ok).
|
||||||
|
@ -176,6 +177,29 @@ record_count() ->
|
||||||
%% Internal functions
|
%% 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) ->
|
do_get_rules(Key) ->
|
||||||
case mnesia:dirty_read(?ACL_TABLE, Key) of
|
case mnesia:dirty_read(?ACL_TABLE, Key) of
|
||||||
[#emqx_acl{rules = Rules}] -> {ok, Rules};
|
[#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, []) -> nomatch;
|
||||||
do_authorize(Client, PubSub, Topic, [ {Permission, Action, TopicFilter} | Tail]) ->
|
do_authorize(Client, PubSub, Topic, [ {Permission, Action, TopicFilter} | Tail]) ->
|
||||||
case emqx_authz_rule:match(Client, PubSub, Topic,
|
Rule = emqx_authz_rule:compile({Permission, all, Action, [TopicFilter]}),
|
||||||
emqx_authz_rule:compile({Permission, all, Action, [TopicFilter]})
|
case emqx_authz_rule:match(Client, PubSub, Topic, Rule) of
|
||||||
) of
|
|
||||||
{matched, Permission} -> {matched, Permission};
|
{matched, Permission} -> {matched, Permission};
|
||||||
nomatch -> do_authorize(Client, PubSub, Topic, Tail)
|
nomatch -> do_authorize(Client, PubSub, Topic, Tail)
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -38,7 +38,7 @@ end_per_suite(_Config) ->
|
||||||
ok = emqx_authz_test_lib:restore_authorizers(),
|
ok = emqx_authz_test_lib:restore_authorizers(),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
|
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(),
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,8 @@
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
|
|
||||||
-include("emqx_authz.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").
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
@ -31,86 +29,123 @@ groups() ->
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ok = emqx_common_test_helpers:start_apps(
|
ok = emqx_common_test_helpers:start_apps(
|
||||||
[emqx_connector, emqx_conf, emqx_authz],
|
[emqx_conf, emqx_authz],
|
||||||
fun set_special_configs/1
|
fun set_special_configs/1),
|
||||||
),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
{ok, _} = emqx:update_config(
|
ok = emqx_authz_test_lib:restore_authorizers(),
|
||||||
[authorization],
|
ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
|
||||||
#{<<"no_match">> => <<"allow">>,
|
|
||||||
<<"cache">> => #{<<"enable">> => <<"true">>},
|
init_per_testcase(_TestCase, Config) ->
|
||||||
<<"sources">> => []}),
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authz, emqx_conf]),
|
ok = setup_config(),
|
||||||
ok.
|
Config.
|
||||||
|
|
||||||
|
end_per_testcase(_TestCase, _Config) ->
|
||||||
|
ok = emqx_authz_mnesia:purge_rules().
|
||||||
|
|
||||||
set_special_configs(emqx_authz) ->
|
set_special_configs(emqx_authz) ->
|
||||||
{ok, _} = emqx:update_config([authorization, cache, enable], false),
|
ok = emqx_authz_test_lib:reset_authorizers();
|
||||||
{ok, _} = emqx:update_config([authorization, no_match], deny),
|
|
||||||
{ok, _} = emqx:update_config([authorization, sources],
|
set_special_configs(_) ->
|
||||||
[#{<<"type">> => <<"built-in-database">>}]),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
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
|
%% Testcases
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
t_username_topic_rules(_Config) ->
|
||||||
|
ok = test_topic_rules(username).
|
||||||
|
|
||||||
t_authz(_) ->
|
t_clientid_topic_rules(_Config) ->
|
||||||
ClientInfo1 = #{clientid => <<"test">>,
|
ok = test_topic_rules(clientid).
|
||||||
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}
|
|
||||||
},
|
|
||||||
|
|
||||||
?assertEqual(deny, emqx_access_control:authorize(
|
t_all_topic_rules(_Config) ->
|
||||||
ClientInfo1, subscribe, <<"#">>)),
|
ok = test_topic_rules(all).
|
||||||
?assertEqual(deny, emqx_access_control:authorize(
|
|
||||||
ClientInfo1, publish, <<"#">>)),
|
|
||||||
|
|
||||||
?assertEqual(allow, emqx_access_control:authorize(
|
test_topic_rules(Key) ->
|
||||||
ClientInfo2, publish, <<"test/test_username">>)),
|
ClientInfo = #{clientid => <<"clientid">>,
|
||||||
?assertEqual(allow, emqx_access_control:authorize(
|
username => <<"username">>,
|
||||||
ClientInfo2, subscribe, <<"#">>)),
|
peerhost => {127,0,0,1},
|
||||||
|
zone => default,
|
||||||
|
listener => {tcp, default}
|
||||||
|
},
|
||||||
|
|
||||||
?assertEqual(allow, emqx_access_control:authorize(
|
SetupSamples = fun(CInfo, Samples) ->
|
||||||
ClientInfo3, publish, <<"test/test_clientid">>)),
|
setup_client_samples(CInfo, Samples, Key)
|
||||||
?assertEqual(deny, emqx_access_control:authorize(
|
end,
|
||||||
ClientInfo3, subscribe, <<"#">>)),
|
|
||||||
|
|
||||||
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(), #{}).
|
||||||
|
|
|
@ -56,7 +56,7 @@ end_per_suite(_Config) ->
|
||||||
ok = stop_apps([emqx_resource, emqx_connector]),
|
ok = stop_apps([emqx_resource, emqx_connector]),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
|
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(),
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ end_per_suite(_Config) ->
|
||||||
ok = stop_apps([emqx_resource, emqx_connector]),
|
ok = stop_apps([emqx_resource, emqx_connector]),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
|
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(),
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ end_per_suite(_Config) ->
|
||||||
ok = stop_apps([emqx_resource, emqx_connector]),
|
ok = stop_apps([emqx_resource, emqx_connector]),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
|
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(),
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ test_samples(ClientInfo, Samples) ->
|
||||||
test_no_topic_rules(ClientInfo, SetupSamples) ->
|
test_no_topic_rules(ClientInfo, SetupSamples) ->
|
||||||
%% No rules
|
%% No rules
|
||||||
|
|
||||||
|
ok = reset_authorizers(deny, false),
|
||||||
ok = SetupSamples(ClientInfo, []),
|
ok = SetupSamples(ClientInfo, []),
|
||||||
|
|
||||||
ok = test_samples(
|
ok = test_samples(
|
||||||
|
|
Loading…
Reference in New Issue