diff --git a/apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf b/apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf index d6587c203..ad41b06a7 100644 --- a/apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf +++ b/apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf @@ -197,4 +197,14 @@ its own from which a browser should permit loading resources.""" zh: "多语言支持" } } + bootstrap_user { + desc { + en: "Initialize users file." + zh: "初始化用户文件" + } + label { + en: "Initialize users file" + zh: "初始化用户文件" + } + } } diff --git a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl index 0650062f8..0df1e4e9e 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -50,7 +50,8 @@ -export([ add_default_user/0, - default_username/0 + default_username/0, + add_bootstrap_user/0 ]). -type emqx_admin() :: #?ADMIN{}. @@ -74,6 +75,28 @@ mnesia(boot) -> ]} ]). +%%-------------------------------------------------------------------- +%% bootstrap API +%%-------------------------------------------------------------------- + +-spec add_default_user() -> {ok, map() | empty | default_user_exists} | {error, any()}. +add_default_user() -> + add_default_user(binenv(default_username), binenv(default_password)). + +-spec add_bootstrap_user() -> ok | {error, _}. +add_bootstrap_user() -> + case emqx:get_config([dashboard, bootstrap_user], undefined) of + undefined -> + ok; + File -> + case mnesia:table_info(?ADMIN, size) of + 0 -> + add_bootstrap_user(File); + _ -> + ok + end + end. + %%-------------------------------------------------------------------- %% API %%-------------------------------------------------------------------- @@ -272,11 +295,6 @@ destroy_token_by_username(Username, Token) -> %%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- - --spec add_default_user() -> {ok, map() | empty | default_user_exists} | {error, any()}. -add_default_user() -> - add_default_user(binenv(default_username), binenv(default_password)). - default_username() -> binenv(default_username). @@ -290,3 +308,35 @@ add_default_user(Username, Password) -> [] -> add_user(Username, Password, <<"administrator">>); _ -> {ok, default_user_exists} end. + +add_bootstrap_user(File) -> + case file:open(File, read) of + {ok, Dev} -> + {ok, MP} = re:compile(<<"(\.+):(\.+)">>), + try + load_bootstrap_user(Dev, MP) + catch + Type:Reason -> + {error, {Type, Reason}} + after + file:close(Dev) + end; + Error -> + Error + end. + +load_bootstrap_user(Dev, MP) -> + case file:read_line(Dev) of + {ok, Line} -> + case re:run(Line, MP, [global, {capture, all_but_first, binary}]) of + {match, Captured} -> + [add_user(Username, Password, <<>>) || [Username, Password] <- Captured]; + _ -> + ok + end, + load_bootstrap_user(Dev, MP); + eof -> + ok; + Error -> + Error + end. diff --git a/apps/emqx_dashboard/src/emqx_dashboard_app.erl b/apps/emqx_dashboard/src/emqx_dashboard_app.erl index 08bfe1d21..4d9c1416a 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_app.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_app.erl @@ -31,6 +31,7 @@ start(_StartType, _StartArgs) -> case emqx_dashboard:start_listeners() of ok -> emqx_dashboard_cli:load(), + ok = emqx_dashboard_admin:add_bootstrap_user(), {ok, _} = emqx_dashboard_admin:add_default_user(), {ok, Sup}; {error, Reason} -> diff --git a/apps/emqx_dashboard/src/emqx_dashboard_schema.erl b/apps/emqx_dashboard/src/emqx_dashboard_schema.erl index 55a1fd38c..4bb9fb6af 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_schema.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_schema.erl @@ -54,7 +54,8 @@ fields("dashboard") -> } )}, {cors, fun cors/1}, - {i18n_lang, fun i18n_lang/1} + {i18n_lang, fun i18n_lang/1}, + {bootstrap_user, ?HOCON(binary(), #{desc => ?DESC(bootstrap_user), required => false})} ]; fields("listeners") -> [