diff --git a/.github/workflows/run_api_tests.yaml b/.github/workflows/run_api_tests.yaml index c315ec23b..fbc6ae6f8 100644 --- a/.github/workflows/run_api_tests.yaml +++ b/.github/workflows/run_api_tests.yaml @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@v2 with: repository: emqx/emqx-fvt - ref: v1.5.0 + ref: 1.0.2-dev1 path: . - uses: actions/setup-java@v1 with: diff --git a/apps/emqx/test/emqx_common_test_http.erl b/apps/emqx/test/emqx_common_test_http.erl index 27fcdc268..3e91f3afd 100644 --- a/apps/emqx/test/emqx_common_test_http.erl +++ b/apps/emqx/test/emqx_common_test_http.erl @@ -19,14 +19,14 @@ -include_lib("common_test/include/ct.hrl"). -export([ request_api/3 - , request_api/4 - , request_api/5 - , get_http_data/1 - , create_default_app/0 - , delete_default_app/0 - , default_auth_header/0 - , auth_header/2 -]). + , request_api/4 + , request_api/5 + , get_http_data/1 + , create_default_app/0 + , delete_default_app/0 + , default_auth_header/0 + , auth_header/2 + ]). request_api(Method, Url, Auth) -> request_api(Method, Url, [], Auth, []). @@ -57,15 +57,14 @@ do_request_api(Method, Request, HttpOpts) -> case httpc:request(Method, Request, HttpOpts, [{body_format, binary}]) of {error, socket_closed_remotely} -> {error, socket_closed_remotely}; - {ok, {{"HTTP/1.1", Code, _}, _Headers, Return} } - when Code =:= 200 orelse Code =:= 201 -> - {ok, Return}; + {ok, {{"HTTP/1.1", Code, _}, _Headers, Return} } -> + {ok, Code, Return}; {ok, {Reason, _, _}} -> {error, Reason} end. get_http_data(ResponseBody) -> - maps:get(<<"data">>, emqx_json:decode(ResponseBody, [return_maps])). + emqx_json:decode(ResponseBody, [return_maps]). auth_header(User, Pass) -> Encoded = base64:encode_to_string(lists:append([User,":",Pass])), diff --git a/apps/emqx_dashboard/include/emqx_dashboard.hrl b/apps/emqx_dashboard/include/emqx_dashboard.hrl index 395234bbf..db7bc8007 100644 --- a/apps/emqx_dashboard/include/emqx_dashboard.hrl +++ b/apps/emqx_dashboard/include/emqx_dashboard.hrl @@ -18,7 +18,7 @@ -record(?ADMIN, { username :: binary(), pwdhash :: binary(), - tags :: list() | binary(), + description :: binary(), role = undefined :: atom(), extra = [] :: term() %% not used so far, for future extension }). diff --git a/apps/emqx_dashboard/src/emqx_dashboard.erl b/apps/emqx_dashboard/src/emqx_dashboard.erl index 7e24c73ab..7bf6f0f5a 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard.erl @@ -40,7 +40,7 @@ start_listeners() -> Authorization = {?MODULE, authorize_appid}, GlobalSpec = #{ openapi => "3.0.0", - info => #{title => "EMQ X Dashboard API", version => "5.0.0"}, + info => #{title => "EMQ X API", version => "5.0.0"}, servers => [#{url => ?BASE_PATH}], components => #{ schemas => #{}, diff --git a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl index 68ddac651..3b32eccfd 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -19,6 +19,7 @@ -module(emqx_dashboard_admin). -include("emqx_dashboard.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). -boot_mnesia({mnesia, [boot]}). @@ -63,17 +64,17 @@ mnesia(boot) -> %% API %%-------------------------------------------------------------------- --spec(add_user(binary(), binary(), binary()) -> ok | {error, any()}). -add_user(Username, Password, Tags) when is_binary(Username), is_binary(Password) -> - Admin = #?ADMIN{username = Username, pwdhash = hash(Password), tags = Tags}, - return(mria:transaction(?DASHBOARD_SHARD, fun add_user_/1, [Admin])). +-spec(add_user(binary(), binary(), binary()) -> {ok, map()} | {error, any()}). +add_user(Username, Password, Desc) + when is_binary(Username), is_binary(Password) -> + return(mria:transaction(?DASHBOARD_SHARD, fun add_user_/3, [Username, Password, Desc])). %% black-magic: force overwrite a user -force_add_user(Username, Password, Tags) -> +force_add_user(Username, Password, Desc) -> AddFun = fun() -> mnesia:write(#?ADMIN{username = Username, pwdhash = hash(Password), - tags = Tags}) + description = Desc}) end, case mria:transaction(?DASHBOARD_SHARD, AddFun) of {atomic, ok} -> ok; @@ -81,33 +82,38 @@ force_add_user(Username, Password, Tags) -> end. %% @private -add_user_(Admin = #?ADMIN{username = Username}) -> +add_user_(Username, Password, Desc) -> case mnesia:wread({?ADMIN, Username}) of - [] -> mnesia:write(Admin); - [_] -> mnesia:abort(<<"Username Already Exist">>) + [] -> + Admin = #?ADMIN{username = Username, pwdhash = hash(Password), description = Desc}, + mnesia:write(Admin), + #{username => Username, description => Desc}; + [_] -> + mnesia:abort(<<"Username Already Exist">>) end. --spec(remove_user(binary()) -> ok | {error, any()}). +-spec(remove_user(binary()) -> {ok, any()} | {error, any()}). remove_user(Username) when is_binary(Username) -> Trans = fun() -> case lookup_user(Username) of - [] -> - mnesia:abort(<<"Username Not Found">>); - _ -> ok - end, - mnesia:delete({?ADMIN, Username}) + [] -> mnesia:abort(<<"Username Not Found">>); + _ -> mnesia:delete({?ADMIN, Username}) + end end, return(mria:transaction(?DASHBOARD_SHARD, Trans)). --spec(update_user(binary(), binary()) -> ok | {error, term()}). -update_user(Username, Tags) when is_binary(Username) -> - return(mria:transaction(?DASHBOARD_SHARD, fun update_user_/2, [Username, Tags])). +-spec(update_user(binary(), binary()) -> {ok, map()} | {error, term()}). +update_user(Username, Desc) when is_binary(Username) -> + return(mria:transaction(?DASHBOARD_SHARD, fun update_user_/2, [Username, Desc])). %% @private -update_user_(Username, Tags) -> +update_user_(Username, Desc) -> case mnesia:wread({?ADMIN, Username}) of - [] -> mnesia:abort(<<"Username Not Found">>); - [Admin] -> mnesia:write(Admin#?ADMIN{tags = Tags}) + [] -> + mnesia:abort(<<"Username Not Found">>); + [Admin] -> + mnesia:write(Admin#?ADMIN{description = Desc}), + #{username => Username, description => Desc} end. change_password(Username, OldPasswd, NewPasswd) when is_binary(Username) -> @@ -146,17 +152,15 @@ lookup_user(Username) when is_binary(Username) -> -spec(all_users() -> [map()]). all_users() -> lists:map(fun(#?ADMIN{username = Username, - tags = Tags + description = Desc }) -> #{username => Username, - %% named tag but not tags, for unknown reason - %% TODO: fix this comment - tag => Tags + description => Desc } end, ets:tab2list(?ADMIN)). -return({atomic, _}) -> - ok; +return({atomic, Result}) -> + {ok, Result}; return({aborted, Reason}) -> {error, Reason}. @@ -219,5 +223,5 @@ add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY add_default_user(Username, Password) -> case lookup_user(Username) of [] -> add_user(Username, Password, <<"administrator">>); - _ -> ok + _ -> {ok, default_user_exists} end. diff --git a/apps/emqx_dashboard/src/emqx_dashboard_api.erl b/apps/emqx_dashboard/src/emqx_dashboard_api.erl index a3f58f4e4..cb3545da3 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_api.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_api.erl @@ -37,14 +37,21 @@ -define(EMPTY(V), (V == undefined orelse V == <<>>)). -define(ERROR_USERNAME_OR_PWD, 'ERROR_USERNAME_OR_PWD'). +-define(USER_NOT_FOUND_BODY, #{ code => <<"USER_NOT_FOUND">> + , message => <<"User not found">>}). + namespace() -> "dashboard". api_spec() -> emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}). -paths() -> ["/login", "/logout", "/users", - "/users/:username", "/users/:username/change_pwd"]. +paths() -> + [ "/login" + , "/logout" + , "/users" + , "/users/:username" + , "/users/:username/change_pwd"]. schema("/login") -> #{ @@ -53,8 +60,7 @@ schema("/login") -> tags => [<<"dashboard">>], description => <<"Dashboard Auth">>, summary => <<"Dashboard Auth">>, - 'requestBody' => - [ + 'requestBody' => [ {username, mk(binary(), #{desc => <<"The User for which to create the token.">>, 'maxLength' => 100, example => <<"admin">>})}, @@ -67,10 +73,12 @@ schema("/login") -> {license, [{edition, mk(enum([community, enterprise]), #{desc => <<"license">>, example => "community"})}]}, - {version, mk(string(), #{desc => <<"version">>, example => <<"5.0.0">>})}], + {version, mk(string(), #{desc => <<"version">>, example => <<"5.0.0">>})} + ], 401 => [ {code, mk(string(), #{example => 'ERROR_USERNAME_OR_PWD'})}, - {message, mk(string(), #{example => "Unauthorized"})}] + {message, mk(string(), #{example => "Unauthorized"})} + ] }, security => [] }}; @@ -86,7 +94,7 @@ schema("/logout") -> 'maxLength' => 100, example => <<"admin">>})} ], responses => #{ - 200 => <<"Dashboard logout successfully">> + 204 => <<"Dashboard logout successfully">> } } }; @@ -95,10 +103,10 @@ schema("/users") -> 'operationId' => users, get => #{ tags => [<<"dashboard">>], - description => <<"Get dashboard users">>, + description => <<"Get dashboard users list">>, responses => #{ - 200 => mk(array(ref(?MODULE, user)), - #{desc => "User lists"}) + 200 => mk( array(ref(?MODULE, user)) + , #{desc => "User lists"}) } }, post => #{ @@ -106,9 +114,12 @@ schema("/users") -> description => <<"Create dashboard users">>, 'requestBody' => fields(user_password), responses => #{ - 200 => <<"Create user successfully">>, + 200 => mk( ref(?MODULE, user) + , #{desc => <<"Create User successfully">>}), 400 => [{code, mk(string(), #{example => 'CREATE_FAIL'})}, - {message, mk(string(), #{example => "Create user failed"})}]} + {message, mk(string(), #{example => "Create user failed"})} + ] + } } }; @@ -120,11 +131,20 @@ schema("/users/:username") -> description => <<"Update dashboard users">>, parameters => [{username, mk(binary(), #{in => path, example => <<"admin">>})}], - 'requestBody' => [{tag, mk(binary(), #{desc => <<"Tag">>})}], + 'requestBody' => [ + { description + , mk(binary(), #{desc => <<"User description">>, example => <<"administrator">>})} + ], responses => #{ - 200 => <<"Update User successfully">>, - 400 => [{code, mk(string(), #{example => 'UPDATE_FAIL'})}, - {message, mk(string(), #{example => "Update Failed unknown"})}]}}, + 200 => mk( ref(?MODULE, user) + , #{desc => <<"Update User successfully">>}), + 400 => [ + {code, mk(string(), #{example => 'UPDATE_FAIL'})}, + {message, mk(string(), #{example => "Update Failed unknown"})} + ], + 404 => emqx_dashboard_swagger:error_codes(['USER_NOT_FOUND'], <<"User Not Found">>) + } + }, delete => #{ tags => [<<"dashboard">>], description => <<"Delete dashboard users">>, @@ -134,7 +154,11 @@ schema("/users/:username") -> 204 => <<"Delete User successfully">>, 400 => [ {code, mk(string(), #{example => 'CANNOT_DELETE_ADMIN'})}, - {message, mk(string(), #{example => "CANNOT DELETE ADMIN"})}]}} + {message, mk(string(), #{example => "CANNOT DELETE ADMIN"})} + ], + 404 => emqx_dashboard_swagger:error_codes(['USER_NOT_FOUND'], <<"User Not Found">>) + } + } }; schema("/users/:username/change_pwd") -> #{ @@ -149,23 +173,26 @@ schema("/users/:username/change_pwd") -> {new_pwd, mk(binary(), #{required => true})} ], responses => #{ - 200 => <<"Update user password successfully">>, + 204 => <<"Update user password successfully">>, 400 => [ {code, mk(string(), #{example => 'UPDATE_FAIL'})}, - {message, mk(string(), #{example => "Failed Reason"})}]}} + {message, mk(string(), #{example => "Failed Reason"})} + ] + } + } }. fields(user) -> [ - {tag, + {description, mk(binary(), - #{desc => <<"tag">>, example => "administrator"})}, + #{desc => <<"User description">>, example => "administrator"})}, {username, mk(binary(), #{desc => <<"username">>, example => "emqx"})} ]; fields(user_password) -> - fields(user) ++ [{password, mk(binary(), #{desc => "Password"})}]. + fields(user) ++ [{password, mk(binary(), #{desc => "Password", example => <<"public">>})}]. login(post, #{body := Params}) -> Username = maps:get(<<"username">>, Params), @@ -182,7 +209,7 @@ logout(_, #{body := #{<<"username">> := Username}, headers := #{<<"authorization">> := <<"Bearer ", Token/binary>>}}) -> case emqx_dashboard_admin:destroy_token_by_username(Username, Token) of ok -> - 200; + 204; _R -> {401, 'BAD_TOKEN_OR_USERNAME', <<"Ensure your token & username">>} end. @@ -191,7 +218,7 @@ users(get, _Request) -> {200, emqx_dashboard_admin:all_users()}; users(post, #{body := Params}) -> - Tag = maps:get(<<"tag">>, Params), + Desc = maps:get(<<"description">>, Params), Username = maps:get(<<"username">>, Params), Password = maps:get(<<"password">>, Params), case ?EMPTY(Username) orelse ?EMPTY(Password) of @@ -199,35 +226,43 @@ users(post, #{body := Params}) -> {400, #{code => <<"CREATE_USER_FAIL">>, message => <<"Username or password undefined">>}}; false -> - case emqx_dashboard_admin:add_user(Username, Password, Tag) of - ok -> {200}; + case emqx_dashboard_admin:add_user(Username, Password, Desc) of + {ok, Result} -> + {200, Result}; {error, Reason} -> {400, #{code => <<"CREATE_USER_FAIL">>, message => Reason}} end end. user(put, #{bindings := #{username := Username}, body := Params}) -> - Tag = maps:get(<<"tag">>, Params), - case emqx_dashboard_admin:update_user(Username, Tag) of - ok -> {200}; - {error, Reason} -> - {400, #{code => <<"UPDATE_FAIL">>, message => Reason}} + Desc = maps:get(<<"description">>, Params), + case emqx_dashboard_admin:update_user(Username, Desc) of + {ok, Result} -> + {200, Result}; + {error, _Reason} -> + {404, ?USER_NOT_FOUND_BODY} end; user(delete, #{bindings := #{username := Username}}) -> case Username == <<"admin">> of - true -> {400, #{code => <<"CANNOT_DELETE_ADMIN">>, - message => <<"Cannot delete admin">>}}; + true -> + {400, #{code => <<"ACTION_NOT_ALLOWED">>, + message => <<"Cannot delete admin">>}}; false -> - _ = emqx_dashboard_admin:remove_user(Username), - {204} + case emqx_dashboard_admin:remove_user(Username) of + {error, _Reason} -> + {404, ?USER_NOT_FOUND_BODY}; + {ok, _} -> + {204} + end end. change_pwd(put, #{bindings := #{username := Username}, body := Params}) -> OldPwd = maps:get(<<"old_pwd">>, Params), NewPwd = maps:get(<<"new_pwd">>, Params), case emqx_dashboard_admin:change_password(Username, OldPwd, NewPwd) of - ok -> {200}; + {ok, _} -> + {204}; {error, Reason} -> {400, #{code => <<"CHANGE_PWD_FAIL">>, message => Reason}} end. diff --git a/apps/emqx_dashboard/src/emqx_dashboard_app.erl b/apps/emqx_dashboard/src/emqx_dashboard_app.erl index 16acdb182..b6fb8dcb8 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_app.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_app.erl @@ -29,7 +29,7 @@ start(_StartType, _StartArgs) -> ok = mria_rlog:wait_for_shards([?DASHBOARD_SHARD], infinity), _ = emqx_dashboard:start_listeners(), emqx_dashboard_cli:load(), - ok = emqx_dashboard_admin:add_default_user(), + {ok, _Result} = emqx_dashboard_admin:add_default_user(), {ok, Sup}. stop(_State) -> diff --git a/apps/emqx_dashboard/src/emqx_dashboard_cli.erl b/apps/emqx_dashboard/src/emqx_dashboard_cli.erl index 14c5009ff..a651b5cac 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_cli.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_cli.erl @@ -27,9 +27,9 @@ load() -> admins(["add", Username, Password]) -> admins(["add", Username, Password, ""]); -admins(["add", Username, Password, Tag]) -> - case emqx_dashboard_admin:add_user(bin(Username), bin(Password), bin(Tag)) of - ok -> +admins(["add", Username, Password, Desc]) -> + case emqx_dashboard_admin:add_user(bin(Username), bin(Password), bin(Desc)) of + {ok, _} -> emqx_ctl:print("ok~n"); {error, already_existed} -> emqx_ctl:print("Error: already existed~n"); @@ -46,9 +46,10 @@ admins(["del", Username]) -> emqx_ctl:print("~p~n", [Status]); admins(_) -> - emqx_ctl:usage([{"admins add ", "Add dashboard user"}, - {"admins passwd ", "Reset dashboard user password"}, - {"admins del ", "Delete dashboard user" }]). + emqx_ctl:usage( + [{"admins add ", "Add dashboard user"}, + {"admins passwd ", "Reset dashboard user password"}, + {"admins del ", "Delete dashboard user" }]). unload() -> emqx_ctl:unregister_command(admins). diff --git a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl index e430b8549..6317ecb76 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl @@ -213,7 +213,7 @@ check_request_body(#{body := Body}, Schema, Module, CheckFun, true) -> check_request_body(#{body := Body}, Spec, _Module, CheckFun, false) -> lists:foldl(fun({Name, Type}, Acc) -> Schema = ?INIT_SCHEMA#{roots => [{Name, Type}]}, - maps:merge(Acc, CheckFun(Schema, Body, #{})) + maps:merge(Acc, CheckFun(Schema, Body, #{override_env => false})) end, #{}, Spec). %% tags, description, summary, security, deprecated diff --git a/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl index f876f7383..146a4e8e5 100644 --- a/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -31,11 +31,14 @@ -define(CONTENT_TYPE, "application/x-www-form-urlencoded"). --define(HOST, "http://127.0.0.1:18083/"). +-define(HOST, "http://127.0.0.1:18083"). --define(API_VERSION, "v4"). +%% -define(API_VERSION, "v5"). --define(BASE_PATH, "api"). +-define(BASE_PATH, "/api/v5"). + +-define(APP_DASHBOARD, emqx_dashboard). +-define(APP_MANAGEMENT, emqx_management). -define(OVERVIEWS, ['alarms/activated', 'alarms/deactivated', @@ -51,9 +54,24 @@ ]). all() -> -%% TODO: V5 API -% emqx_common_test_helpers:all(?MODULE). - [t_cli, t_lookup_by_username_jwt, t_clean_expired_jwt]. + %% TODO: V5 API + %% emqx_common_test_helpers:all(?MODULE). + [t_cli, t_lookup_by_username_jwt, t_clean_expired_jwt, t_rest_api]. + +init_suite() -> + init_suite([]). + +init_suite(Apps) -> + mria:start(), + application:load(emqx_management), + emqx_common_test_helpers:start_apps(Apps ++ [emqx_dashboard], fun set_special_configs/1). + +end_suite() -> + end_suite([]). + +end_suite(Apps) -> + application:unload(emqx_management), + emqx_common_test_helpers:stop_apps(Apps ++ [emqx_dashboard]). init_per_suite(Config) -> emqx_common_test_helpers:start_apps([emqx_management, emqx_dashboard], @@ -66,23 +84,32 @@ end_per_suite(_Config) -> set_special_configs(emqx_management) -> Listeners = [#{protocol => http, port => 8081}], - emqx_config:put([emqx_management], #{listeners => Listeners, - applications =>[#{id => "admin", secret => "public"}]}), + Config = #{listeners => Listeners, + applications => [#{id => "admin", secret => "public"}]}, + emqx_config:put([emqx_management], Config), + ok; +set_special_configs(emqx_dashboard) -> + Listeners = [#{protocol => http, port => 18083}], + Config = #{listeners => Listeners, + default_username => <<"admin">>, + default_password => <<"public">> + }, + emqx_config:put([emqx_dashboard], Config), ok; set_special_configs(_) -> ok. t_overview(_) -> mnesia:clear_table(?ADMIN), - emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"tag">>), + emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"simple_description">>), [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), - auth_header_()))|| Overview <- ?OVERVIEWS]. + auth_header_())) || Overview <- ?OVERVIEWS]. t_admins_add_delete(_) -> mnesia:clear_table(?ADMIN), - Tag = <<"tag">>, - ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Tag), - ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Tag), + Desc = <<"simple description">>, + ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Desc), + ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Desc), Admins = emqx_dashboard_admin:all_users(), ?assertEqual(2, length(Admins)), ok = emqx_dashboard_admin:remove_user(<<"username1">>), @@ -92,7 +119,7 @@ t_admins_add_delete(_) -> <<"password">>, <<"pwd">>), timer:sleep(10), - Header = auth_header_("username", "pwd"), + Header = auth_header_(<<"username">>, <<"pwd">>), ?assert(request_dashboard(get, api_path("brokers"), Header)), ok = emqx_dashboard_admin:remove_user(<<"username">>), @@ -100,28 +127,22 @@ t_admins_add_delete(_) -> t_rest_api(_Config) -> mnesia:clear_table(?ADMIN), - Tag = <<"administrator">>, - emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, Tag), - {ok, Res0} = http_get("users"), - + Desc = <<"administrator">>, + emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, Desc), + {ok, 200, Res0} = http_get(["users"]), ?assertEqual([#{<<"username">> => <<"admin">>, - <<"tags">> => <<"administrator">>}], get_http_data(Res0)), - - AssertSuccess = fun({ok, Res}) -> - ?assertEqual(#{<<"code">> => 0}, json(Res)) - end, - [AssertSuccess(R) - || R <- [ http_put("users/admin", #{<<"tags">> => <<"a_new_tag">>}) - , http_post("users", #{<<"username">> => <<"usera">>, - <<"password">> => <<"passwd">>}) - , http_post("auth", #{<<"username">> => <<"usera">>, - <<"password">> => <<"passwd">>}) - , http_delete("users/usera") - , http_put("users/admin/change_pwd", #{<<"old_pwd">> => <<"public">>, - <<"new_pwd">> => <<"newpwd">>}) - , http_post("auth", #{<<"username">> => <<"admin">>, - <<"password">> => <<"newpwd">>}) - ]], + <<"description">> => <<"administrator">>}], get_http_data(Res0)), + {ok, 200, _} = http_put(["users", "admin"], #{<<"description">> => <<"a_new_description">>}), + {ok, 200, _} = http_post(["users"], #{<<"username">> => <<"usera">>, + <<"password">> => <<"passwd">>, + <<"description">> => Desc}), + {ok, 204, _} = http_delete(["users", "usera"]), + {ok, 404, _} = http_delete(["users", "usera"]), + {ok, 204, _} = http_put( ["users", "admin", "change_pwd"] + , #{<<"old_pwd">> => <<"public">>, + <<"new_pwd">> => <<"newpwd">>}), + mnesia:clear_table(?ADMIN), + emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"administrator">>), ok. t_cli(_Config) -> @@ -175,17 +196,17 @@ bin(X) -> iolist_to_binary(X). random_num() -> erlang:system_time(nanosecond). -http_get(Path) -> - request_api(get, api_path(Path), auth_header_()). +http_get(Parts) -> + request_api(get, api_path(Parts), auth_header_()). -http_delete(Path) -> - request_api(delete, api_path(Path), auth_header_()). +http_delete(Parts) -> + request_api(delete, api_path(Parts), auth_header_()). -http_post(Path, Body) -> - request_api(post, api_path(Path), [], auth_header_(), Body). +http_post(Parts, Body) -> + request_api(post, api_path(Parts), [], auth_header_(), Body). -http_put(Path, Body) -> - request_api(put, api_path(Path), [], auth_header_(), Body). +http_put(Parts, Body) -> + request_api(put, api_path(Parts), [], auth_header_(), Body). request_dashboard(Method, Url, Auth) -> Request = {Url, [Auth]}, @@ -198,21 +219,22 @@ do_request_dashboard(Method, Request)-> case httpc:request(Method, Request, [], []) of {error, socket_closed_remotely} -> {error, socket_closed_remotely}; - {ok, {{"HTTP/1.1", 200, _}, _, _Return} } -> - true; + {ok, {{"HTTP/1.1", Code, _}, _Headers, Return} } + when Code >= 200 andalso Code =< 299 -> + {ok, Return}; {ok, {Reason, _, _}} -> {error, Reason} end. auth_header_() -> - auth_header_("admin", "public"). + auth_header_(<<"admin">>, <<"public">>). -auth_header_(User, Pass) -> - Encoded = base64:encode_to_string(lists:append([User,":",Pass])), - {"Authorization","Basic " ++ Encoded}. +auth_header_(Username, Password) -> + {ok, Token} = emqx_dashboard_admin:sign_token(Username, Password), + {"Authorization","Bearer " ++ binary_to_list(Token)}. -api_path(Path) -> - ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, Path]). +api_path(Parts) -> + ?HOST ++ filename:join([?BASE_PATH | Parts]). json(Data) -> {ok, Jsx} = emqx_json:safe_decode(Data, [return_maps]), Jsx.