feat(management): implement the auth-mnesia import/export

All codes picks from emqx-management:dev/e4.2.3
This commit is contained in:
JianBo He 2020-12-11 18:13:13 +08:00 committed by JianBo He
parent 826419df5f
commit 5d98944c94
5 changed files with 115 additions and 107 deletions

View File

@ -124,21 +124,17 @@
, export_blacklist/0 , export_blacklist/0
, export_applications/0 , export_applications/0
, export_users/0 , export_users/0
, export_auth_clientid/0
, export_auth_username/0
, export_auth_mnesia/0 , export_auth_mnesia/0
, export_acl_mnesia/0 , export_acl_mnesia/0
, export_schemas/0
, import_rules/1 , import_rules/1
, import_resources/1 , import_resources/1
, import_blacklist/1 , import_blacklist/1
, import_applications/1 , import_applications/1
, import_users/1 , import_users/1
, import_auth_clientid/1 , import_auth_clientid/1 %% BACKW: 4.1.x
, import_auth_username/1 , import_auth_username/1 %% BACKW: 4.1.x
, import_auth_mnesia/1 , import_auth_mnesia/2
, import_acl_mnesia/1 , import_acl_mnesia/2
, import_schemas/1
, to_version/1 , to_version/1
]). ]).
@ -612,78 +608,59 @@ export_rules() ->
end, emqx_rule_registry:get_rules()). end, emqx_rule_registry:get_rules()).
export_resources() -> export_resources() ->
lists:foldl(fun({_, Id, Type, Config, CreatedAt, Desc}, Acc) -> lists:map(fun({_, Id, Type, Config, CreatedAt, Desc}) ->
NCreatedAt = case CreatedAt of NCreatedAt = case CreatedAt of
undefined -> null; undefined -> null;
_ -> CreatedAt _ -> CreatedAt
end, end,
[[{id, Id}, [{id, Id},
{type, Type}, {type, Type},
{config, maps:to_list(Config)}, {config, maps:to_list(Config)},
{created_at, NCreatedAt}, {created_at, NCreatedAt},
{description, Desc}] | Acc] {description, Desc}]
end, [], emqx_rule_registry:get_resources()). end, emqx_rule_registry:get_resources()).
export_blacklist() -> export_blacklist() ->
lists:foldl(fun(#banned{who = Who, by = By, reason = Reason, at = At, until = Until}, Acc) -> lists:map(fun(#banned{who = Who, by = By, reason = Reason, at = At, until = Until}) ->
NWho = case Who of NWho = case Who of
{peerhost, Peerhost} -> {peerhost, inet:ntoa(Peerhost)}; {peerhost, Peerhost} -> {peerhost, inet:ntoa(Peerhost)};
_ -> Who _ -> Who
end, end,
[[{who, [NWho]}, {by, By}, {reason, Reason}, {at, At}, {until, Until}] | Acc] [{who, [NWho]}, {by, By}, {reason, Reason}, {at, At}, {until, Until}]
end, [], ets:tab2list(emqx_banned)). end, ets:tab2list(emqx_banned)).
export_applications() -> export_applications() ->
lists:foldl(fun({_, AppID, AppSecret, Name, Desc, Status, Expired}, Acc) -> lists:map(fun({_, AppID, AppSecret, Name, Desc, Status, Expired}) ->
[[{id, AppID}, {secret, AppSecret}, {name, Name}, {desc, Desc}, {status, Status}, {expired, Expired}] | Acc] [{id, AppID}, {secret, AppSecret}, {name, Name}, {desc, Desc}, {status, Status}, {expired, Expired}]
end, [], ets:tab2list(mqtt_app)). end, ets:tab2list(mqtt_app)).
export_users() -> export_users() ->
lists:foldl(fun({_, Username, Password, Tags}, Acc) -> lists:map(fun({_, Username, Password, Tags}) ->
[[{username, Username}, {password, base64:encode(Password)}, {tags, Tags}] | Acc] [{username, Username}, {password, base64:encode(Password)}, {tags, Tags}]
end, [], ets:tab2list(mqtt_admin)). end, ets:tab2list(mqtt_admin)).
export_auth_clientid() ->
case ets:info(emqx_auth_clientid) of
undefined -> [];
_ ->
lists:foldl(fun({_, ClientId, Password}, Acc) ->
[[{clientid, ClientId}, {password, Password}] | Acc]
end, [], ets:tab2list(emqx_auth_clientid))
end.
export_auth_username() ->
case ets:info(emqx_auth_username) of
undefined -> [];
_ ->
lists:foldl(fun({_, Username, Password}, Acc) ->
[[{username, Username}, {password, Password}] | Acc]
end, [], ets:tab2list(emqx_auth_username))
end.
export_auth_mnesia() -> export_auth_mnesia() ->
case ets:info(emqx_user) of case ets:info(emqx_user) of
undefined -> []; undefined -> [];
_ -> _ ->
lists:foldl(fun({_, Login, Password, IsSuperuser}, Acc) -> lists:map(fun({_, {Type, Login}, Password, CreatedAt}) ->
[[{login, Login}, {password, Password}, {is_superuser, IsSuperuser}] | Acc] [{login, Login}, {type, Type}, {password, base64:encode(Password)}, {created_at, CreatedAt}]
end, [], ets:tab2list(emqx_user)) end, ets:tab2list(emqx_user))
end. end.
export_acl_mnesia() -> export_acl_mnesia() ->
case ets:info(emqx_acl) of case ets:info(emqx_acl) of
undefined -> []; undefined -> [];
_ -> _ ->
lists:foldl(fun({_, Login, Topic, Action, Allow}, Acc) -> lists:map(fun({_, Filter, Action, Access, CreatedAt}) ->
[[{login, Login}, {topic, Topic}, {action, Action}, {allow, Allow}] | Acc] Filter1 = case Filter of
end, [], ets:tab2list(emqx_acl)) {{Type, TypeValue}, Topic} ->
end. [{type, Type}, {type_value, TypeValue}, {topic, Topic}];
{Type, Topic} ->
export_schemas() -> [{type, Type}, {topic, Topic}]
case ets:info(emqx_schema) of end,
undefined -> []; Filter1 ++ [{action, Action}, {access, Access}, {created_at, CreatedAt}]
_ -> end, ets:tab2list(emqx_acl))
[emqx_schema_api:format_schema(Schema) || Schema <- emqx_schema_registry:get_all_schemas()]
end. end.
import_rules(Rules) -> import_rules(Rules) ->
@ -761,44 +738,80 @@ import_users(Users) ->
end, Users). end, Users).
import_auth_clientid(Lists) -> import_auth_clientid(Lists) ->
case ets:info(emqx_auth_clientid) of
undefined -> ok;
_ ->
[ mnesia:dirty_write({emqx_auth_clientid, ClientId, Password}) || #{<<"clientid">> := ClientId,
<<"password">> := Password} <- Lists ]
end.
import_auth_username(Lists) ->
case ets:info(emqx_auth_username) of
undefined -> ok;
_ ->
[ mnesia:dirty_write({emqx_auth_username, Username, Password}) || #{<<"username">> := Username,
<<"password">> := Password} <- Lists ]
end.
import_auth_mnesia(Auths) ->
case ets:info(emqx_user) of case ets:info(emqx_user) of
undefined -> ok; undefined -> ok;
_ -> _ ->
[ mnesia:dirty_write({emqx_user, Login, Password, IsSuperuser}) || #{<<"login">> := Login, [ mnesia:dirty_write({emqx_user, {clientid, Clientid}, base64:decode(Password), erlang:system_time(millisecond)})
<<"password">> := Password, || #{<<"clientid">> := Clientid, <<"password">> := Password} <- Lists ]
<<"is_superuser">> := IsSuperuser} <- Auths ]
end. end.
import_acl_mnesia(Acls) -> import_auth_username(Lists) ->
case ets:info(emqx_user) of
undefined -> ok;
_ ->
[ mnesia:dirty_write({emqx_user, {username, Username}, base64:decode(Password), erlang:system_time(millisecond)})
|| #{<<"username">> := Username, <<"password">> := Password} <- Lists ]
end.
import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse
FromVersion =:= "4.1" ->
case ets:info(emqx_user) of
undefined -> ok;
_ ->
CreatedAt = erlang:system_time(millisecond),
[ begin
mnesia:dirty_write({emqx_user, {username, Login}, base64:decode(Password), CreatedAt})
end
|| #{<<"login">> := Login,
<<"password">> := Password} <- Auths ]
end;
import_auth_mnesia(Auths, _) ->
case ets:info(emqx_user) of
undefined -> ok;
_ ->
[ mnesia:dirty_write({emqx_user, {any_to_atom(Type), Login}, base64:decode(Password), CreatedAt})
|| #{<<"login">> := Login,
<<"type">> := Type,
<<"password">> := Password,
<<"created_at">> := CreatedAt } <- Auths ]
end.
import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.0" orelse
FromVersion =:= "4.1" ->
case ets:info(emqx_acl) of case ets:info(emqx_acl) of
undefined -> ok; undefined -> ok;
_ -> _ ->
[ mnesia:dirty_write({emqx_acl ,Login, Topic, Action, Allow}) || #{<<"login">> := Login, CreatedAt = erlang:system_time(millisecond),
<<"topic">> := Topic, [begin
<<"action">> := Action, Allow1 = case any_to_atom(Allow) of
<<"allow">> := Allow} <- Acls ] true -> allow;
end. false -> deny
end,
mnesia:dirty_write({emqx_acl, {{username, Login}, Topic}, any_to_atom(Action), Allow1, CreatedAt})
end || #{<<"login">> := Login,
<<"topic">> := Topic,
<<"allow">> := Allow,
<<"action">> := Action} <- Acls]
end;
import_schemas(Schemas) -> import_acl_mnesia(Acls, _) ->
case ets:info(emqx_schema) of case ets:info(emqx_acl) of
undefined -> ok; undefined -> ok;
_ -> [emqx_schema_registry:add_schema(emqx_schema_api:make_schema_params(Schema)) || Schema <- Schemas] _ ->
[ begin
Filter = case maps:get(<<"type_value">>, Map, undefined) of
undefined ->
{any_to_atom(maps:get(<<"type">>, Map)), maps:get(<<"topic">>, Map)};
Value ->
{{any_to_atom(maps:get(<<"type">>, Map)), Value}, maps:get(<<"topic">>, Map)}
end,
mnesia:dirty_write({emqx_acl ,Filter, any_to_atom(Action), any_to_atom(Access), CreatedAt})
end
|| Map = #{<<"action">> := Action,
<<"access">> := Access,
<<"created_at">> := CreatedAt} <- Acls ]
end. end.
any_to_atom(L) when is_list(L) -> list_to_atom(L); any_to_atom(L) when is_list(L) -> list_to_atom(L);

View File

@ -70,13 +70,13 @@ add_app(_Bindings, Params) ->
Expired = get_value(<<"expired">>, Params), Expired = get_value(<<"expired">>, Params),
case emqx_mgmt_auth:add_app(AppId, Name, Secret, Desc, Status, Expired) of case emqx_mgmt_auth:add_app(AppId, Name, Secret, Desc, Status, Expired) of
{ok, AppSecret} -> return({ok, #{secret => AppSecret}}); {ok, AppSecret} -> return({ok, #{secret => AppSecret}});
{error, Reason} -> return({error, ?ERROR2, Reason}) {error, Reason} -> return({error, Reason})
end. end.
del_app(#{appid := AppId}, _Params) -> del_app(#{appid := AppId}, _Params) ->
case emqx_mgmt_auth:del_app(AppId) of case emqx_mgmt_auth:del_app(AppId) of
ok -> return(); ok -> return();
{error, Reason} -> return({error, ?ERROR2, Reason}) {error, Reason} -> return({error, Reason})
end. end.
list_apps(_Bindings, _Params) -> list_apps(_Bindings, _Params) ->
@ -102,7 +102,7 @@ update_app(#{appid := AppId}, Params) ->
Expired = get_value(<<"expired">>, Params), Expired = get_value(<<"expired">>, Params),
case emqx_mgmt_auth:update_app(AppId, Name, Desc, Status, Expired) of case emqx_mgmt_auth:update_app(AppId, Name, Desc, Status, Expired) of
ok -> return(); ok -> return();
{error, Reason} -> return({error, ?ERROR2, Reason}) {error, Reason} -> return({error, Reason})
end. end.
format({AppId, _AppSecret, Name, Desc, Status, Expired}) -> format({AppId, _AppSecret, Name, Desc, Status, Expired}) ->

View File

@ -80,11 +80,8 @@ export(_Bindings, _Params) ->
Blacklist = emqx_mgmt:export_blacklist(), Blacklist = emqx_mgmt:export_blacklist(),
Apps = emqx_mgmt:export_applications(), Apps = emqx_mgmt:export_applications(),
Users = emqx_mgmt:export_users(), Users = emqx_mgmt:export_users(),
AuthClientid = emqx_mgmt:export_auth_clientid(),
AuthUsername = emqx_mgmt:export_auth_username(),
AuthMnesia = emqx_mgmt:export_auth_mnesia(), AuthMnesia = emqx_mgmt:export_auth_mnesia(),
AclMnesia = emqx_mgmt:export_acl_mnesia(), AclMnesia = emqx_mgmt:export_acl_mnesia(),
Schemas = emqx_mgmt:export_schemas(),
Seconds = erlang:system_time(second), Seconds = erlang:system_time(second),
{{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds), {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]), Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
@ -97,11 +94,8 @@ export(_Bindings, _Params) ->
{blacklist, Blacklist}, {blacklist, Blacklist},
{apps, Apps}, {apps, Apps},
{users, Users}, {users, Users},
{auth_clientid, AuthClientid},
{auth_username, AuthUsername},
{auth_mnesia, AuthMnesia}, {auth_mnesia, AuthMnesia},
{acl_mnesia, AclMnesia}, {acl_mnesia, AclMnesia}
{schemas, Schemas}
], ],
Bin = emqx_json:encode(Data), Bin = emqx_json:encode(Data),
@ -180,18 +174,18 @@ do_import(Filename) ->
case lists:member(Version, ?VERSIONS) of case lists:member(Version, ?VERSIONS) of
true -> true ->
try try
emqx_mgmt:import_confs(maps:get(<<"configs">>, Data, []), maps:get(<<"listeners_state">>, Data, [])), %emqx_mgmt:import_confs(maps:get(<<"configs">>, Data, []), maps:get(<<"listeners_state">>, Data, [])),
emqx_mgmt:import_resources(maps:get(<<"resources">>, Data, [])), emqx_mgmt:import_resources(maps:get(<<"resources">>, Data, [])),
emqx_mgmt:import_rules(maps:get(<<"rules">>, Data, [])), emqx_mgmt:import_rules(maps:get(<<"rules">>, Data, [])),
emqx_mgmt:import_blacklist(maps:get(<<"blacklist">>, Data, [])), emqx_mgmt:import_blacklist(maps:get(<<"blacklist">>, Data, [])),
emqx_mgmt:import_applications(maps:get(<<"apps">>, Data, [])), emqx_mgmt:import_applications(maps:get(<<"apps">>, Data, [])),
emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])), emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])),
emqx_mgmt:import_modules(maps:get(<<"modules">>, Data, [])), %emqx_mgmt:import_modules(maps:get(<<"modules">>, Data, [])),
_ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])), _ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
_ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])), _ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])),
_ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version), _ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
_ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version), _ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
_ = emqx_mgmt:import_schemas(maps:get(<<"schemas">>, Data, [])), %_ = emqx_mgmt:import_schemas(maps:get(<<"schemas">>, Data, [])),
logger:debug("The emqx data has been imported successfully"), logger:debug("The emqx data has been imported successfully"),
ok ok
catch Class:Reason:Stack -> catch Class:Reason:Stack ->

View File

@ -563,11 +563,8 @@ data(["export"]) ->
Blacklist = emqx_mgmt:export_blacklist(), Blacklist = emqx_mgmt:export_blacklist(),
Apps = emqx_mgmt:export_applications(), Apps = emqx_mgmt:export_applications(),
Users = emqx_mgmt:export_users(), Users = emqx_mgmt:export_users(),
AuthClientID = emqx_mgmt:export_auth_clientid(),
AuthUsername = emqx_mgmt:export_auth_username(),
AuthMnesia = emqx_mgmt:export_auth_mnesia(), AuthMnesia = emqx_mgmt:export_auth_mnesia(),
AclMnesia = emqx_mgmt:export_acl_mnesia(), AclMnesia = emqx_mgmt:export_acl_mnesia(),
Schemas = emqx_mgmt:export_schemas(),
Seconds = erlang:system_time(second), Seconds = erlang:system_time(second),
{{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds), {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]), Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
@ -580,11 +577,8 @@ data(["export"]) ->
{blacklist, Blacklist}, {blacklist, Blacklist},
{apps, Apps}, {apps, Apps},
{users, Users}, {users, Users},
{auth_clientid, AuthClientID},
{auth_username, AuthUsername},
{auth_mnesia, AuthMnesia}, {auth_mnesia, AuthMnesia},
{acl_mnesia, AclMnesia}, {acl_mnesia, AclMnesia}],
{schemas, Schemas}],
ok = filelib:ensure_dir(NFilename), ok = filelib:ensure_dir(NFilename),
case file:write_file(NFilename, emqx_json:encode(Data)) of case file:write_file(NFilename, emqx_json:encode(Data)) of
ok -> ok ->
@ -608,9 +602,8 @@ data(["import", Filename]) ->
emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])), emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])),
_ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])), _ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
_ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])), _ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])),
_ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, [])), _ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
_ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, [])), _ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
_ = emqx_mgmt:import_schemas(maps:get(<<"schemas">>, Data, [])),
emqx_ctl:print("The emqx data has been imported successfully.~n") emqx_ctl:print("The emqx data has been imported successfully.~n")
catch Class:Reason:Stack -> catch Class:Reason:Stack ->
emqx_ctl:print("The emqx data import failed due: ~0p~n", [{Class,Reason,Stack}]) emqx_ctl:print("The emqx data import failed due: ~0p~n", [{Class,Reason,Stack}])

View File

@ -73,7 +73,8 @@ ranch_opts(Port, Options0) ->
socket_opts => [{port, Port} | Options]}, socket_opts => [{port, Port} | Options]},
Res. Res.
stop_listener({Proto, _Port, _}) -> stop_listener({Proto, Port, _}) ->
io:format("Stop http:management listener on ~s successfully.~n",[format(Port)]),
minirest:stop_http(listener_name(Proto)). minirest:stop_http(listener_name(Proto)).
listeners() -> listeners() ->
@ -123,3 +124,10 @@ filter(#{app := App}) ->
false -> false; false -> false;
Plugin -> Plugin#plugin.active Plugin -> Plugin#plugin.active
end. end.
format(Port) when is_integer(Port) ->
io_lib:format("0.0.0.0:~w", [Port]);
format({Addr, Port}) when is_list(Addr) ->
io_lib:format("~s:~w", [Addr, Port]);
format({Addr, Port}) when is_tuple(Addr) ->
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).