diff --git a/apps/emqx_authz/etc/emqx_authz.conf b/apps/emqx_authz/etc/emqx_authz.conf index 1111a7819..28746ebe7 100644 --- a/apps/emqx_authz/etc/emqx_authz.conf +++ b/apps/emqx_authz/etc/emqx_authz.conf @@ -55,6 +55,10 @@ authorization { # collection: mqtt_authz # selector: { "$or": [ { "username": "%u" }, { "clientid": "%c" } ] } # }, + { + type: built-in-database + path: "{{ platform_etc_dir }}/acl.conf" + } { type: file path: "{{ platform_etc_dir }}/acl.conf" diff --git a/apps/emqx_authz/include/emqx_authz.hrl b/apps/emqx_authz/include/emqx_authz.hrl index ad3287611..8ef8899b0 100644 --- a/apps/emqx_authz/include/emqx_authz.hrl +++ b/apps/emqx_authz/include/emqx_authz.hrl @@ -19,15 +19,6 @@ -type(sources() :: [map()]). --define(ACL_SHARDED, emqx_acl_sharded). - --define(ACL_TABLE, emqx_acl). - --record(emqx_acl, { - who :: username() | clientid() | all, - rules :: [ {permission(), action(), emqx_topic:topic()} ] - }). - -define(APP, emqx_authz). -define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= <<"allow">>) orelse @@ -38,6 +29,20 @@ (A =:= all) orelse (A =:= <<"all">>) )). +-define(ACL_SHARDED, emqx_acl_sharded). + +-define(ACL_TABLE, emqx_acl). + +%% To save some space, use an integer for label, 0 for 'all', {1, Username} and {2, ClientId}. +-define(ACL_TABLE_ALL, 0). +-define(ACL_TABLE_USERNAME, 1). +-define(ACL_TABLE_CLIENTID, 2). + +-record(emqx_acl, { + who :: ?ACL_TABLE_ALL| {?ACL_TABLE_USERNAME, binary()} | {?ACL_TABLE_CLIENTID, binary()}, + rules :: [ {permission(), action(), emqx_topic:topic()} ] + }). + -record(authz_metrics, { allow = 'client.authorize.allow', deny = 'client.authorize.deny', diff --git a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl index b8d3cae0e..a77862d30 100644 --- a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl +++ b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl @@ -409,7 +409,7 @@ records(get, #{bindings := #{type := <<"username">>}, query_string := Qs }) -> MatchSpec = ets:fun2ms( - fun({?ACL_TABLE, {username, Username}, Rules}) -> + fun({?ACL_TABLE, {?ACL_TABLE_USERNAME, Username}, Rules}) -> [{username, Username}, {rules, Rules}] end), Format = fun ([{username, Username}, {rules, Rules}]) -> @@ -436,7 +436,7 @@ records(get, #{bindings := #{type := <<"clientid">>}, query_string := Qs }) -> MatchSpec = ets:fun2ms( - fun({?ACL_TABLE, {clientid, Clientid}, Rules}) -> + fun({?ACL_TABLE, {?ACL_TABLE_CLIENTID, Clientid}, Rules}) -> [{clientid, Clientid}, {rules, Rules}] end), Format = fun ([{clientid, Clientid}, {rules, Rules}]) -> @@ -460,7 +460,7 @@ records(get, #{bindings := #{type := <<"clientid">>}, end; records(get, #{bindings := #{type := <<"all">>}}) -> MatchSpec = ets:fun2ms( - fun({?ACL_TABLE, all, Rules}) -> + fun({?ACL_TABLE, ?ACL_TABLE_ALL, Rules}) -> [{rules, Rules}] end), {200, [ #{rules => [ #{topic => Topic, @@ -472,7 +472,7 @@ records(post, #{bindings := #{type := <<"username">>}, body := Body}) when is_list(Body) -> lists:foreach(fun(#{<<"username">> := Username, <<"rules">> := Rules}) -> ekka_mnesia:dirty_write(#emqx_acl{ - who = {username, Username}, + who = {?ACL_TABLE_USERNAME, Username}, rules = format_rules(Rules) }) end, Body), @@ -481,7 +481,7 @@ records(post, #{bindings := #{type := <<"clientid">>}, body := Body}) when is_list(Body) -> lists:foreach(fun(#{<<"clientid">> := Clientid, <<"rules">> := Rules}) -> ekka_mnesia:dirty_write(#emqx_acl{ - who = {clientid, Clientid}, + who = {?ACL_TABLE_CLIENTID, Clientid}, rules = format_rules(Rules) }) end, Body), @@ -489,15 +489,15 @@ records(post, #{bindings := #{type := <<"clientid">>}, records(put, #{bindings := #{type := <<"all">>}, body := #{<<"rules">> := Rules}}) -> ekka_mnesia:dirty_write(#emqx_acl{ - who = all, + who = ?ACL_TABLE_ALL, rules = format_rules(Rules) }), {204}. record(get, #{bindings := #{type := <<"username">>, key := Key}}) -> - case mnesia:dirty_read(?ACL_TABLE, {username, Key}) of + case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_USERNAME, Key}) of [] -> {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}}; - [#emqx_acl{who = {username, Username}, rules = Rules}] -> + [#emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = Rules}] -> {200, #{username => Username, rules => [ #{topic => Topic, action => Action, @@ -506,9 +506,9 @@ record(get, #{bindings := #{type := <<"username">>, key := Key}}) -> } end; record(get, #{bindings := #{type := <<"clientid">>, key := Key}}) -> - case mnesia:dirty_read(?ACL_TABLE, {clientid, Key}) of + case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_CLIENTID, Key}) of [] -> {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}}; - [#emqx_acl{who = {clientid, Clientid}, rules = Rules}] -> + [#emqx_acl{who = {?ACL_TABLE_CLIENTID, Clientid}, rules = Rules}] -> {200, #{clientid => Clientid, rules => [ #{topic => Topic, action => Action, @@ -519,22 +519,22 @@ record(get, #{bindings := #{type := <<"clientid">>, key := Key}}) -> record(put, #{bindings := #{type := <<"username">>, key := Username}, body := #{<<"username">> := Username, <<"rules">> := Rules}}) -> ekka_mnesia:dirty_write(#emqx_acl{ - who = {username, Username}, + who = {?ACL_TABLE_USERNAME, Username}, rules = format_rules(Rules) }), {204}; record(put, #{bindings := #{type := <<"clientid">>, key := Clientid}, body := #{<<"clientid">> := Clientid, <<"rules">> := Rules}}) -> ekka_mnesia:dirty_write(#emqx_acl{ - who = {clientid, Clientid}, + who = {?ACL_TABLE_CLIENTID, Clientid}, rules = format_rules(Rules) }), {204}; record(delete, #{bindings := #{type := <<"username">>, key := Key}}) -> - ekka_mnesia:dirty_delete({?ACL_TABLE, {username, Key}}), + ekka_mnesia:dirty_delete({?ACL_TABLE, {?ACL_TABLE_USERNAME, Key}}), {204}; record(delete, #{bindings := #{type := <<"clientid">>, key := Key}}) -> - ekka_mnesia:dirty_delete({?ACL_TABLE, {clientid, Key}}), + ekka_mnesia:dirty_delete({?ACL_TABLE, {?ACL_TABLE_CLIENTID, Key}}), {204}. format_rules(Rules) when is_list(Rules) -> diff --git a/apps/emqx_authz/src/emqx_authz_mnesia.erl b/apps/emqx_authz/src/emqx_authz_mnesia.erl index b222edfb1..ab755403e 100644 --- a/apps/emqx_authz/src/emqx_authz_mnesia.erl +++ b/apps/emqx_authz/src/emqx_authz_mnesia.erl @@ -52,15 +52,15 @@ authorize(#{username := Username, clientid := Clientid } = Client, PubSub, Topic, #{type := 'built-in-database'}) -> - Rules = case mnesia:dirty_read(?ACL_TABLE, {clientid, Clientid}) of + Rules = case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_CLIENTID, Clientid}) of [] -> []; [#emqx_acl{rules = Rules0}] when is_list(Rules0) -> Rules0 end - ++ case mnesia:dirty_read(?ACL_TABLE, {username, Username}) of + ++ case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_USERNAME, Username}) of [] -> []; [#emqx_acl{rules = Rules1}] when is_list(Rules1) -> Rules1 end - ++ case mnesia:dirty_read(?ACL_TABLE, all) of + ++ case mnesia:dirty_read(?ACL_TABLE, ?ACL_TABLE_ALL) of [] -> []; [#emqx_acl{rules = Rules2}] when is_list(Rules2) -> Rules2 end, diff --git a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl index aa668a2a2..8b221d3e7 100644 --- a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl @@ -54,17 +54,17 @@ end_per_suite(_Config) -> ok. init_per_testcase(t_authz, Config) -> - mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = {username, <<"test_username">>}, + mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = {?ACL_TABLE_USERNAME, <<"test_username">>}, rules = [{allow, publish, <<"test/%u">>}, {allow, subscribe, <<"eq #">>} ] }]), - mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = {clientid, <<"test_clientid">>}, + mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = {?ACL_TABLE_CLIENTID, <<"test_clientid">>}, rules = [{allow, publish, <<"test/%c">>}, {deny, subscribe, <<"eq #">>} ] }]), - mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = all, + mnesia:transaction(fun ekka_mnesia:dirty_write/1, [#emqx_acl{who = ?ACL_TABLE_ALL, rules = [{deny, all, <<"#">>}] }]), Config;