fix(emqx_auth_mnesia): add missing combinations of permissions
Allow to define different access for pub and sub actions
This commit is contained in:
parent
d19eb05234
commit
830cdffe16
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
init() ->
|
init() ->
|
||||||
ok = ekka_mnesia:create_table(emqx_acl, [
|
ok = ekka_mnesia:create_table(emqx_acl, [
|
||||||
|
{type, bag},
|
||||||
{disc_copies, [node()]},
|
{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, emqx_acl)},
|
{attributes, record_info(fields, emqx_acl)},
|
||||||
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
|
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
|
||||||
|
|
|
@ -39,13 +39,19 @@
|
||||||
-spec(add_acl(login() | all, emqx_topic:topic(), pub | sub | pubsub, allow | deny) ->
|
-spec(add_acl(login() | all, emqx_topic:topic(), pub | sub | pubsub, allow | deny) ->
|
||||||
ok | {error, any()}).
|
ok | {error, any()}).
|
||||||
add_acl(Login, Topic, Action, Access) ->
|
add_acl(Login, Topic, Action, Access) ->
|
||||||
Acls = #?TABLE{
|
Filter = {Login, Topic},
|
||||||
filter = {Login, Topic},
|
Acl = #?TABLE{
|
||||||
action = Action,
|
filter = Filter,
|
||||||
access = Access,
|
action = Action,
|
||||||
created_at = erlang:system_time(millisecond)
|
access = Access,
|
||||||
},
|
created_at = erlang:system_time(millisecond)
|
||||||
ret(mnesia:transaction(fun mnesia:write/1, [Acls])).
|
},
|
||||||
|
ret(mnesia:transaction(
|
||||||
|
fun() ->
|
||||||
|
OldRecords = mnesia:wread({?TABLE, Filter}),
|
||||||
|
maybe_delete_shadowed_records(Action, OldRecords),
|
||||||
|
mnesia:write(Acl)
|
||||||
|
end)).
|
||||||
|
|
||||||
%% @doc Lookup acl by login
|
%% @doc Lookup acl by login
|
||||||
-spec(lookup_acl(login() | all) -> list()).
|
-spec(lookup_acl(login() | all) -> list()).
|
||||||
|
@ -233,3 +239,13 @@ print_acl({all, Topic, Action, Access, _}) ->
|
||||||
"Acl($all topic = ~p action = ~p access = ~p)~n",
|
"Acl($all topic = ~p action = ~p access = ~p)~n",
|
||||||
[Topic, Action, Access]
|
[Topic, Action, Access]
|
||||||
).
|
).
|
||||||
|
|
||||||
|
maybe_delete_shadowed_records(_, []) ->
|
||||||
|
ok;
|
||||||
|
maybe_delete_shadowed_records(Action1, [Rec = #emqx_acl{action = Action2} | Rest]) ->
|
||||||
|
if Action1 =:= Action2 orelse Action1 =:= pubsub ->
|
||||||
|
ok = mnesia:delete_object(Rec);
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
maybe_delete_shadowed_records(Action1, Rest).
|
||||||
|
|
|
@ -124,6 +124,7 @@ t_acl_cli(_Config) ->
|
||||||
|
|
||||||
?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))),
|
?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))),
|
||||||
|
|
||||||
|
emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "deny"]),
|
||||||
emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "allow"]),
|
emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "allow"]),
|
||||||
R1 = emqx_ctl:format("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n",
|
R1 = emqx_ctl:format("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n",
|
||||||
[<<"test_clientid">>, <<"topic/A">>, pub, allow]),
|
[<<"test_clientid">>, <<"topic/A">>, pub, allow]),
|
||||||
|
@ -136,6 +137,7 @@ t_acl_cli(_Config) ->
|
||||||
?assertEqual([R2], emqx_acl_mnesia_cli:cli(["show", "username", "test_username"])),
|
?assertEqual([R2], emqx_acl_mnesia_cli:cli(["show", "username", "test_username"])),
|
||||||
?assertEqual([R2], emqx_acl_mnesia_cli:cli(["list", "username"])),
|
?assertEqual([R2], emqx_acl_mnesia_cli:cli(["list", "username"])),
|
||||||
|
|
||||||
|
emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pub", "allow"]),
|
||||||
emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pubsub", "deny"]),
|
emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pubsub", "deny"]),
|
||||||
?assertMatch(["Acl($all topic = <<\"#\">> action = pubsub access = deny)\n"],
|
?assertMatch(["Acl($all topic = <<\"#\">> action = pubsub access = deny)\n"],
|
||||||
emqx_acl_mnesia_cli:cli(["list", "_all"])
|
emqx_acl_mnesia_cli:cli(["list", "_all"])
|
||||||
|
|
|
@ -441,9 +441,11 @@ import_acl_mnesia(Acls, _) ->
|
||||||
do_import_acl_mnesia(Acls).
|
do_import_acl_mnesia(Acls).
|
||||||
-else.
|
-else.
|
||||||
import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse
|
import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse
|
||||||
FromVersion =:= "4.1" orelse
|
FromVersion =:= "4.1" ->
|
||||||
FromVersion =:= "4.2" ->
|
|
||||||
do_import_auth_mnesia_by_old_data(Auths);
|
do_import_auth_mnesia_by_old_data(Auths);
|
||||||
|
import_auth_mnesia(Auths, "4.2") ->
|
||||||
|
%% 4.2 contains a bug where password is not base64-encoded
|
||||||
|
do_import_auth_mnesia_4_2(Auths);
|
||||||
import_auth_mnesia(Auths, _) ->
|
import_auth_mnesia(Auths, _) ->
|
||||||
do_import_auth_mnesia(Auths).
|
do_import_auth_mnesia(Auths).
|
||||||
|
|
||||||
|
@ -454,6 +456,17 @@ import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.0" orelse
|
||||||
|
|
||||||
import_acl_mnesia(Acls, _) ->
|
import_acl_mnesia(Acls, _) ->
|
||||||
do_import_acl_mnesia(Acls).
|
do_import_acl_mnesia(Acls).
|
||||||
|
|
||||||
|
do_import_auth_mnesia_4_2(Auths) ->
|
||||||
|
case ets:info(emqx_user) of
|
||||||
|
undefined -> ok;
|
||||||
|
_ ->
|
||||||
|
CreatedAt = erlang:system_time(millisecond),
|
||||||
|
lists:foreach(fun(#{<<"login">> := Login,
|
||||||
|
<<"password">> := Password}) ->
|
||||||
|
mnesia:dirty_write({emqx_user, {username, Login}, Password, CreatedAt})
|
||||||
|
end, Auths)
|
||||||
|
end.
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
do_import_auth_mnesia_by_old_data(Auths) ->
|
do_import_auth_mnesia_by_old_data(Auths) ->
|
||||||
|
@ -466,6 +479,8 @@ do_import_auth_mnesia_by_old_data(Auths) ->
|
||||||
mnesia:dirty_write({emqx_user, {username, Login}, base64:decode(Password), CreatedAt})
|
mnesia:dirty_write({emqx_user, {username, Login}, base64:decode(Password), CreatedAt})
|
||||||
end, Auths)
|
end, Auths)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
do_import_auth_mnesia(Auths) ->
|
do_import_auth_mnesia(Auths) ->
|
||||||
case ets:info(emqx_user) of
|
case ets:info(emqx_user) of
|
||||||
undefined -> ok;
|
undefined -> ok;
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 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_auth_mnesia_migration_SUITE).
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_mqtt.hrl").
|
||||||
|
-include_lib("emqx_auth_mnesia/include/emqx_auth_mnesia.hrl").
|
||||||
|
|
||||||
|
all() ->
|
||||||
|
[t_import_4_2, t_import_4_1].
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
emqx_ct_helpers:start_apps([emqx_management, emqx_dashboard, emqx_auth_mnesia]),
|
||||||
|
ekka_mnesia:start(),
|
||||||
|
emqx_mgmt_auth:mnesia(boot),
|
||||||
|
mnesia:clear_table(emqx_acl),
|
||||||
|
mnesia:clear_table(emqx_user),
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_suite(_Config) ->
|
||||||
|
emqx_ct_helpers:stop_apps([emqx_modules, emqx_management, emqx_dashboard, emqx_management, emqx_auth_mnesia]),
|
||||||
|
ekka_mnesia:ensure_stopped().
|
||||||
|
|
||||||
|
init_per_testcase(_, Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_testcase(_, _Config) ->
|
||||||
|
mnesia:clear_table(emqx_acl),
|
||||||
|
mnesia:clear_table(emqx_user),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_import_4_2(Config) ->
|
||||||
|
test_import(Config, "v4.2.json").
|
||||||
|
|
||||||
|
t_import_4_1(Config) ->
|
||||||
|
test_import(Config, "v4.1.json").
|
||||||
|
|
||||||
|
test_import(Config, File) ->
|
||||||
|
Filename = filename:join(proplists:get_value(data_dir, Config), File),
|
||||||
|
?assertMatch(ok, emqx_mgmt_data_backup:import(Filename)),
|
||||||
|
Records = lists:sort(ets:tab2list(emqx_acl)),
|
||||||
|
%% Check importing of records related to emqx_auth_mnesia
|
||||||
|
?assertMatch([#emqx_acl{
|
||||||
|
filter = {{username,<<"emqx_c">>}, <<"Topic/A">>},
|
||||||
|
action = pub,
|
||||||
|
access = allow
|
||||||
|
},
|
||||||
|
#emqx_acl{
|
||||||
|
filter = {{username,<<"emqx_c">>}, <<"Topic/A">>},
|
||||||
|
action = sub,
|
||||||
|
access = allow
|
||||||
|
}],
|
||||||
|
lists:sort(Records)),
|
||||||
|
?assertMatch([#emqx_user{
|
||||||
|
login = {username, <<"emqx_c">>}
|
||||||
|
}], ets:tab2list(emqx_user)),
|
||||||
|
Req = #{clientid => "blah",
|
||||||
|
username => <<"emqx_c">>,
|
||||||
|
password => "emqx_p"
|
||||||
|
},
|
||||||
|
?assertMatch({stop, #{auth_result := success}},
|
||||||
|
emqx_auth_mnesia:check(Req, #{}, #{hash_type => sha256})).
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"acl_mnesia": [
|
||||||
|
{
|
||||||
|
"action": "sub",
|
||||||
|
"allow": true,
|
||||||
|
"login": "emqx_c",
|
||||||
|
"topic": "Topic/A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "pub",
|
||||||
|
"allow": true,
|
||||||
|
"login": "emqx_c",
|
||||||
|
"topic": "Topic/A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"desc": "Application user",
|
||||||
|
"expired": "undefined",
|
||||||
|
"id": "admin",
|
||||||
|
"name": "Default",
|
||||||
|
"secret": "public",
|
||||||
|
"status": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"auth_clientid": [],
|
||||||
|
"auth_mnesia": [
|
||||||
|
{
|
||||||
|
"is_superuser": false,
|
||||||
|
"login": "emqx_c",
|
||||||
|
"password": "Y2ViNWU5MTdmNzkzMGFlOGYwZGMzY2ViNDk2YTQyOGY3ZTY0NDczNmVlYmNhMzZhMmI4ZjZiYmFjNzU2MTcxYQ=="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"auth_username": [],
|
||||||
|
"blacklist": [],
|
||||||
|
"date": "2021-03-30 09:11:29",
|
||||||
|
"resources": [],
|
||||||
|
"rules": [],
|
||||||
|
"schemas": [],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"password": "t89PhgOb15rSCdpxm7Obp7QGcyY=",
|
||||||
|
"tags": "administrator",
|
||||||
|
"username": "admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "4.1"
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"schemas": [],
|
||||||
|
"acl_mnesia": [
|
||||||
|
{
|
||||||
|
"allow": true,
|
||||||
|
"action": "sub",
|
||||||
|
"topic": "Topic/A",
|
||||||
|
"login": "emqx_c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow": true,
|
||||||
|
"action": "pub",
|
||||||
|
"topic": "Topic/A",
|
||||||
|
"login": "emqx_c"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"auth_mnesia": [
|
||||||
|
{
|
||||||
|
"is_superuser": false,
|
||||||
|
"password": "ceb5e917f7930ae8f0dc3ceb496a428f7e644736eebca36a2b8f6bbac756171a",
|
||||||
|
"login": "emqx_c"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"auth_username": [],
|
||||||
|
"auth_clientid": [],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"tags": "viewer",
|
||||||
|
"password": "oVqjR1wOi2u4DtsuXNctYt6+SKE=",
|
||||||
|
"username": "test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tags": "administrator",
|
||||||
|
"password": "9SO4rEEZ6rNwA4vAwp3cnXgQsAM=",
|
||||||
|
"username": "admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"expired": "undefined",
|
||||||
|
"status": true,
|
||||||
|
"desc": "Application user",
|
||||||
|
"name": "Default",
|
||||||
|
"secret": "public",
|
||||||
|
"id": "admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blacklist": [],
|
||||||
|
"resources": [],
|
||||||
|
"rules": [],
|
||||||
|
"date": "2021-03-26 09:51:38",
|
||||||
|
"version": "4.2"
|
||||||
|
}
|
Loading…
Reference in New Issue