Merge pull request #9586 from lafirest/fix/disable_basic_auth_api

fix: disable basic auth for HTTP API
This commit is contained in:
lafirest 2023-01-12 23:02:29 +08:00 committed by GitHub
commit a26c05f4f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 185 additions and 208 deletions

View File

@ -201,12 +201,25 @@ jobs:
echo "waiting emqx started";
sleep 10;
done
- name: Get Token
timeout-minutes: 1
run: |
kubectl port-forward service/${{ matrix.profile }} 18083:18083 > /dev/null &
while
[ "$(curl --silent -X 'GET' 'http://127.0.0.1:18083/api/v5/status' | tail -n1)" != "emqx is running" ]
do
echo "waiting emqx"
sleep 1
done
echo "TOKEN=$(curl --silent -X 'POST' 'http://127.0.0.1:18083/api/v5/login' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{"username": "admin","password": "public"}' | jq -r ".token")" >> $GITHUB_ENV
- name: Check cluster
timeout-minutes: 10
run: |
kubectl port-forward service/${{ matrix.profile }} 18083:18083 > /dev/null &
while
[ "$(curl --silent --basic -u admin:public -X GET http://127.0.0.1:18083/api/v5/cluster| jq '.nodes|length')" != "3" ];
[ "$(curl --silent -H "Authorization: Bearer $TOKEN" -X GET http://127.0.0.1:18083/api/v5/cluster| jq '.nodes|length')" != "3" ];
do
echo "waiting ${{ matrix.profile }} cluster scale"
sleep 1

View File

@ -92,7 +92,7 @@ jobs:
- uses: actions/checkout@v3
with:
repository: emqx/emqx-fvt
ref: broker-autotest-v2
ref: broker-autotest-v4
path: scripts
- uses: actions/setup-java@v3
with:
@ -191,7 +191,7 @@ jobs:
- uses: actions/checkout@v3
with:
repository: emqx/emqx-fvt
ref: broker-autotest-v2
ref: broker-autotest-v4
path: scripts
- uses: actions/setup-java@v3
with:
@ -297,7 +297,7 @@ jobs:
- uses: actions/checkout@v3
with:
repository: emqx/emqx-fvt
ref: broker-autotest-v2
ref: broker-autotest-v4
path: scripts
- uses: actions/setup-java@v3
with:
@ -396,7 +396,7 @@ jobs:
- uses: actions/checkout@v3
with:
repository: emqx/emqx-fvt
ref: broker-autotest-v2
ref: broker-autotest-v4
path: scripts
- name: run jwks_server
timeout-minutes: 10
@ -496,7 +496,7 @@ jobs:
- uses: actions/checkout@v3
with:
repository: emqx/emqx-fvt
ref: broker-autotest-v2
ref: broker-autotest-v4
path: scripts
- uses: actions/setup-java@v3
with:

View File

@ -15,10 +15,8 @@
%%--------------------------------------------------------------------
%% HTTP API Auth
-define(WRONG_USERNAME_OR_PWD, 'WRONG_USERNAME_OR_PWD').
-define(WRONG_USERNAME_OR_PWD_OR_API_KEY_OR_API_SECRET,
'WRONG_USERNAME_OR_PWD_OR_API_KEY_OR_API_SECRET'
).
-define(BAD_USERNAME_OR_PWD, 'BAD_USERNAME_OR_PWD').
-define(BAD_API_KEY_OR_SECRET, 'BAD_API_KEY_OR_SECRET').
%% Bad Request
-define(BAD_REQUEST, 'BAD_REQUEST').
@ -57,8 +55,8 @@
%% All codes
-define(ERROR_CODES, [
{'WRONG_USERNAME_OR_PWD', <<"Wrong username or pwd">>},
{'WRONG_USERNAME_OR_PWD_OR_API_KEY_OR_API_SECRET', <<"Wrong username & pwd or key & secret">>},
{?BAD_USERNAME_OR_PWD, <<"Bad username or password">>},
{?BAD_API_KEY_OR_SECRET, <<"Bad API key or secret">>},
{'BAD_REQUEST', <<"Request parameters are not legal">>},
{'NOT_MATCH', <<"Conditions are not matched">>},
{'ALREADY_EXISTS', <<"Resource already existed">>},

View File

@ -29,6 +29,9 @@
auth_header/2
]).
-define(DEFAULT_APP_ID, <<"default_appid">>).
-define(DEFAULT_APP_SECRET, <<"default_app_secret">>).
request_api(Method, Url, Auth) ->
request_api(Method, Url, [], Auth, []).
@ -74,12 +77,18 @@ auth_header(User, Pass) ->
{"Authorization", "Basic " ++ Encoded}.
default_auth_header() ->
AppId = <<"myappid">>,
AppSecret = emqx_mgmt_auth:get_appsecret(AppId),
auth_header(erlang:binary_to_list(AppId), erlang:binary_to_list(AppSecret)).
{ok, #{api_key := APIKey}} = emqx_mgmt_auth:read(?DEFAULT_APP_ID),
auth_header(
erlang:binary_to_list(APIKey), erlang:binary_to_list(?DEFAULT_APP_SECRET)
).
create_default_app() ->
emqx_mgmt_auth:add_app(<<"myappid">>, <<"test">>).
Now = erlang:system_time(second),
ExpiredAt = Now + timer:minutes(10),
emqx_mgmt_auth:create(
?DEFAULT_APP_ID, ?DEFAULT_APP_SECRET, true, ExpiredAt, <<"default app key for test">>
),
ok.
delete_default_app() ->
emqx_mgmt_auth:del_app(<<"myappid">>).
emqx_mgmt_auth:delete(?DEFAULT_APP_ID).

View File

@ -18,7 +18,8 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1, multipart_formdata_request/3]).
-import(emqx_dashboard_api_test_helpers, [multipart_formdata_request/3]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
-include("emqx_authn.hrl").
-include_lib("eunit/include/eunit.hrl").
@ -65,9 +66,8 @@ end_per_testcase(_, Config) ->
init_per_suite(Config) ->
emqx_config:erase(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY),
_ = application:load(emqx_conf),
ok = emqx_common_test_helpers:start_apps(
[emqx_authn, emqx_dashboard],
fun set_special_configs/1
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_authn]
),
?AUTHN:delete_chain(?GLOBAL),
@ -76,12 +76,7 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authn]),
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config();
set_special_configs(_App) ->
emqx_mgmt_api_test_util:end_suite([emqx_authn]),
ok.
%%------------------------------------------------------------------------------

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/2, uri/1]).
-import(emqx_mgmt_api_test_util, [request/2, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -32,8 +32,8 @@ groups() ->
[].
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authz, emqx_dashboard, emqx_management],
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_authz],
fun set_special_configs/1
),
Config.
@ -47,7 +47,7 @@ end_per_suite(_Config) ->
<<"sources">> => []
}
),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf, emqx_management]),
emqx_mgmt_api_test_util:end_suite([emqx_authz, emqx_conf]),
ok.
set_special_configs(emqx_dashboard) ->
@ -67,12 +67,12 @@ t_clean_cahce(_) ->
ok = emqtt:publish(C, <<"a/b/c">>, <<"{\"x\":1,\"y\":1}">>, 0),
{ok, 200, Result3} = request(get, uri(["clients", "emqx0", "authorization", "cache"])),
?assertEqual(2, length(jsx:decode(Result3))),
?assertEqual(2, length(emqx_json:decode(Result3))),
request(delete, uri(["authorization", "cache"])),
{ok, 200, Result4} = request(get, uri(["clients", "emqx0", "authorization", "cache"])),
?assertEqual(0, length(jsx:decode(Result4))),
?assertEqual(0, length(emqx_json:decode(Result4))),
ok.

View File

@ -22,7 +22,7 @@
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
all() ->
emqx_common_test_helpers:all(?MODULE).
@ -31,8 +31,8 @@ groups() ->
[].
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authz, emqx_dashboard],
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_authz],
fun set_special_configs/1
),
Config.
@ -46,7 +46,7 @@ end_per_suite(_Config) ->
<<"sources">> => []
}
),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf]),
emqx_mgmt_api_test_util:end_suite([emqx_authz, emqx_conf]),
ok.
set_special_configs(emqx_dashboard) ->

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -30,7 +30,7 @@ groups() ->
[].
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_authz, emqx_dashboard],
fun set_special_configs/1
),
@ -46,7 +46,7 @@ end_per_suite(_Config) ->
}
),
ok = stop_apps([emqx_resource]),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf]),
emqx_mgmt_api_test_util:end_suite([emqx_authz, emqx_conf]),
ok.
set_special_configs(emqx_dashboard) ->

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -115,8 +115,8 @@ init_per_suite(Config) ->
end
),
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authz, emqx_dashboard],
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_authz],
fun set_special_configs/1
),
ok = start_apps([emqx_resource]),
@ -134,7 +134,7 @@ end_per_suite(_Config) ->
%% resource and connector should be stop first,
%% or authz_[mysql|pgsql|redis..]_SUITE would be failed
ok = stop_apps([emqx_resource]),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf]),
emqx_mgmt_api_test_util:end_suite([emqx_authz, emqx_conf]),
meck:unload(emqx_resource),
ok.

View File

@ -93,9 +93,8 @@ init_per_suite(Config) ->
" }"
>>
),
emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_dashboard, ?APP],
fun set_special_configs/1
emqx_mgmt_api_test_util:init_suite(
[emqx_conf, ?APP]
),
Config.
@ -111,12 +110,6 @@ end_per_testcase(t_get_basic_usage_info, _Config) ->
end_per_testcase(_TestCase, _Config) ->
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config(),
ok;
set_special_configs(_) ->
ok.
topic_config(T) ->
#{
topic => T,
@ -132,7 +125,7 @@ end_per_suite(_) ->
application:unload(?APP),
meck:unload(emqx_resource),
meck:unload(emqx_schema),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_conf, ?APP]).
emqx_mgmt_api_test_util:end_suite([emqx_conf, ?APP]).
t_auto_subscribe(_) ->
emqx_auto_subscribe:update([#{<<"topic">> => Topic} || Topic <- ?TOPICS]),

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/4, uri/1]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -60,9 +60,8 @@ init_per_suite(Config) ->
%% some testcases (may from other app) already get emqx_connector started
_ = application:stop(emqx_resource),
_ = application:stop(emqx_connector),
ok = emqx_common_test_helpers:start_apps(
[emqx_rule_engine, emqx_bridge, emqx_dashboard],
fun set_special_configs/1
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_rule_engine, emqx_bridge]
),
ok = emqx_common_test_helpers:load_config(
emqx_rule_engine_schema,
@ -72,12 +71,7 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_rule_engine, emqx_bridge, emqx_dashboard]),
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config(<<"bridge_admin">>);
set_special_configs(_) ->
emqx_mgmt_api_test_util:end_suite([emqx_rule_engine, emqx_bridge]),
ok.
init_per_testcase(_, Config) ->
@ -605,9 +599,6 @@ t_with_redact_update(_Config) ->
?assertEqual(Password, Value),
ok.
request(Method, Url, Body) ->
request(<<"bridge_admin">>, Method, Url, Body).
operation_path(node, Oper, BridgeID) ->
uri(["nodes", node(), "bridges", BridgeID, "operation", Oper]);
operation_path(cluster, Oper, BridgeID) ->

View File

@ -65,8 +65,12 @@ start_listeners(Listeners) ->
components => #{
schemas => #{},
'securitySchemes' => #{
'basicAuth' => #{type => http, scheme => basic},
'bearerAuth' => #{type => http, scheme => bearer}
'basicAuth' => #{
type => http,
scheme => basic,
description =>
<<"Authorize with [API Keys](https://www.emqx.io/docs/en/v5.0/admin/api.html#api-keys)">>
}
}
}
},
@ -215,28 +219,7 @@ listener_name(Protocol) ->
authorize(Req) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of
{basic, Username, Password} ->
case emqx_dashboard_admin:check(Username, Password) of
ok ->
ok;
{error, <<"username_not_found">>} ->
Path = cowboy_req:path(Req),
case emqx_mgmt_auth:authorize(Path, Username, Password) of
ok ->
ok;
{error, <<"not_allowed">>} ->
return_unauthorized(
?WRONG_USERNAME_OR_PWD,
<<"Check username/password">>
);
{error, _} ->
return_unauthorized(
?WRONG_USERNAME_OR_PWD_OR_API_KEY_OR_API_SECRET,
<<"Check username/password or api_key/api_secret">>
)
end;
{error, _} ->
return_unauthorized(?WRONG_USERNAME_OR_PWD, <<"Check username/password">>)
end;
api_key_authorize(Req, Username, Password);
{bearer, Token} ->
case emqx_dashboard_admin:verify_token(Token) of
ok ->
@ -269,3 +252,20 @@ i18n_file() ->
listeners() ->
emqx_conf:get([dashboard, listeners], []).
api_key_authorize(Req, Key, Secret) ->
Path = cowboy_req:path(Req),
case emqx_mgmt_auth:authorize(Path, Key, Secret) of
ok ->
ok;
{error, <<"not_allowed">>} ->
return_unauthorized(
?BAD_API_KEY_OR_SECRET,
<<"Not allowed, Check api_key/api_secret">>
);
{error, _} ->
return_unauthorized(
?BAD_API_KEY_OR_SECRET,
<<"Check api_key/api_secret">>
)
end.

View File

@ -47,7 +47,7 @@
-define(EMPTY(V), (V == undefined orelse V == <<>>)).
-define(WRONG_USERNAME_OR_PWD, 'WRONG_USERNAME_OR_PWD').
-define(BAD_USERNAME_OR_PWD, 'BAD_USERNAME_OR_PWD').
-define(WRONG_TOKEN_OR_USERNAME, 'WRONG_TOKEN_OR_USERNAME').
-define(USER_NOT_FOUND, 'USER_NOT_FOUND').
-define(ERROR_PWD_NOT_MATCH, 'ERROR_PWD_NOT_MATCH').
@ -164,7 +164,7 @@ schema("/users/:username/change_pwd") ->
}.
response_schema(401) ->
emqx_dashboard_swagger:error_codes([?WRONG_USERNAME_OR_PWD], ?DESC(login_failed401));
emqx_dashboard_swagger:error_codes([?BAD_USERNAME_OR_PWD], ?DESC(login_failed401));
response_schema(404) ->
emqx_dashboard_swagger:error_codes([?USER_NOT_FOUND], ?DESC(users_api404)).
@ -223,7 +223,7 @@ login(post, #{body := Params}) ->
}};
{error, R} ->
?SLOG(info, #{msg => "Dashboard login failed", username => Username, reason => R}),
{401, ?WRONG_USERNAME_OR_PWD, <<"Auth failed">>}
{401, ?BAD_USERNAME_OR_PWD, <<"Auth failed">>}
end.
logout(_, #{

View File

@ -114,9 +114,9 @@ t_admin_delete_self_failed(_) ->
?assertEqual(1, length(Admins)),
Header = auth_header_(<<"username1">>, <<"password">>),
{error, {_, 400, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header),
Token = erlang:iolist_to_binary(["Basic ", base64:encode("username1:password")]),
Token = ["Basic ", base64:encode("username1:password")],
Header2 = {"Authorization", Token},
{error, {_, 400, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header2),
{error, {_, 401, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header2),
mnesia:clear_table(?ADMIN).
t_rest_api(_Config) ->

View File

@ -25,43 +25,24 @@
-define(SERVER, "http://127.0.0.1:18083/api/v5").
-import(emqx_mgmt_api_test_util, [request/2]).
all() ->
emqx_common_test_helpers:all(?MODULE).
init_per_suite(Config) ->
mria:start(),
application:load(emqx_dashboard),
emqx_common_test_helpers:start_apps([emqx_conf, emqx_dashboard], fun set_special_configs/1),
emqx_mgmt_api_test_util:init_suite([emqx_conf]),
Config.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config(),
ok;
set_special_configs(_) ->
ok.
end_per_suite(Config) ->
end_suite(),
Config.
end_suite() ->
application:unload(emqx_management),
emqx_common_test_helpers:stop_apps([emqx_dashboard]).
emqx_mgmt_api_test_util:end_suite([emqx_conf]).
t_bad_api_path(_) ->
Url = ?SERVER ++ "/for/test/some/path/not/exist",
{error, {"HTTP/1.1", 404, "Not Found"}} = request(Url),
{ok, 404, _} = request(get, Url),
ok.
request(Url) ->
Request = {Url, []},
case httpc:request(get, Request, [], []) of
{error, Reason} ->
{error, Reason};
{ok, {{"HTTP/1.1", Code, _}, _, Return}} when
Code >= 200 andalso Code =< 299
->
{ok, emqx_json:decode(Return, [return_maps])};
{ok, {Reason, _, _}} ->
{error, Reason}
end.

View File

@ -19,6 +19,8 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_SUITE, [auth_header_/0]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("emqx/include/emqx.hrl").
@ -153,10 +155,6 @@ do_request_api(Method, Request) ->
{error, Reason}
end.
auth_header_() ->
Basic = binary_to_list(base64:encode(<<"admin:public">>)),
{"Authorization", "Basic " ++ Basic}.
restart_monitor() ->
OldMonitor = erlang:whereis(emqx_dashboard_monitor),
erlang:exit(OldMonitor, kill),

View File

@ -347,13 +347,7 @@ do_request_api(Method, Request) ->
end.
auth_header_() ->
AppId = <<"admin">>,
AppSecret = <<"public">>,
auth_header_(binary_to_list(AppId), binary_to_list(AppSecret)).
auth_header_(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User, ":", Pass])),
{"Authorization", "Basic " ++ Encoded}.
emqx_mgmt_api_test_util:auth_header_().
api_path(Parts) ->
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts).

View File

@ -106,8 +106,6 @@ assert_fields_exist(Ks, Map) ->
%% http
-define(http_api_host, "http://127.0.0.1:18083/api/v5").
-define(default_user, "admin").
-define(default_pass, "public").
request(delete = Mth, Path) ->
do_request(Mth, req(Path, []));
@ -176,5 +174,4 @@ url(Path, Qs) ->
lists:concat([?http_api_host, Path, "?", binary_to_list(cow_qs:qs(Qs))]).
auth(Headers) ->
Token = base64:encode(?default_user ++ ":" ++ ?default_pass),
[{"Authorization", "Basic " ++ binary_to_list(Token)}] ++ Headers.
[emqx_mgmt_api_test_util:auth_header_() | Headers].

View File

@ -40,6 +40,10 @@
do_force_create_app/3
]).
-ifdef(TEST).
-export([create/5]).
-endif.
-define(APP, emqx_app).
-record(?APP, {
@ -68,8 +72,12 @@ init_bootstrap_file() ->
init_bootstrap_file(File).
create(Name, Enable, ExpiredAt, Desc) ->
ApiSecret = generate_api_secret(),
create(Name, ApiSecret, Enable, ExpiredAt, Desc).
create(Name, ApiSecret, Enable, ExpiredAt, Desc) ->
case mnesia:table_info(?APP, size) < 100 of
true -> create_app(Name, Enable, ExpiredAt, Desc);
true -> create_app(Name, ApiSecret, Enable, ExpiredAt, Desc);
false -> {error, "Maximum ApiKey"}
end.
@ -157,8 +165,7 @@ to_map(#?APP{name = N, api_key = K, enable = E, expired_at = ET, created_at = CT
is_expired(undefined) -> false;
is_expired(ExpiredTime) -> ExpiredTime < erlang:system_time(second).
create_app(Name, Enable, ExpiredAt, Desc) ->
ApiSecret = generate_api_secret(),
create_app(Name, ApiSecret, Enable, ExpiredAt, Desc) ->
App =
#?APP{
name = Name,
@ -170,9 +177,10 @@ create_app(Name, Enable, ExpiredAt, Desc) ->
api_key = list_to_binary(emqx_misc:gen_id(16))
},
case create_app(App) of
{error, api_key_already_existed} -> create_app(Name, Enable, ExpiredAt, Desc);
{ok, Res} -> {ok, Res#{api_secret => ApiSecret}};
Error -> Error
{ok, Res} ->
{ok, Res#{api_secret => ApiSecret}};
Error ->
Error
end.
create_app(App = #?APP{api_key = ApiKey, name = Name}) ->

View File

@ -225,21 +225,23 @@ t_create_unexpired_app(_Config) ->
ok.
list_app() ->
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
Path = emqx_mgmt_api_test_util:api_path(["api_key"]),
case emqx_mgmt_api_test_util:request_api(get, Path) of
case emqx_mgmt_api_test_util:request_api(get, Path, AuthHeader) of
{ok, Apps} -> {ok, emqx_json:decode(Apps, [return_maps])};
Error -> Error
end.
read_app(Name) ->
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
Path = emqx_mgmt_api_test_util:api_path(["api_key", Name]),
case emqx_mgmt_api_test_util:request_api(get, Path) of
case emqx_mgmt_api_test_util:request_api(get, Path, AuthHeader) of
{ok, Res} -> {ok, emqx_json:decode(Res, [return_maps])};
Error -> Error
end.
create_app(Name) ->
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
Path = emqx_mgmt_api_test_util:api_path(["api_key"]),
ExpiredAt = to_rfc3339(erlang:system_time(second) + 1000),
App = #{
@ -254,7 +256,7 @@ create_app(Name) ->
end.
create_unexpired_app(Name, Params) ->
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
Path = emqx_mgmt_api_test_util:api_path(["api_key"]),
App = maps:merge(#{name => Name, desc => <<"Note"/utf8>>, enable => true}, Params),
case emqx_mgmt_api_test_util:request_api(post, Path, "", AuthHeader, App) of
@ -263,11 +265,12 @@ create_unexpired_app(Name, Params) ->
end.
delete_app(Name) ->
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
DeletePath = emqx_mgmt_api_test_util:api_path(["api_key", Name]),
emqx_mgmt_api_test_util:request_api(delete, DeletePath).
emqx_mgmt_api_test_util:request_api(delete, DeletePath, AuthHeader).
update_app(Name, Change) ->
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
AuthHeader = emqx_dashboard_SUITE:auth_header_(),
UpdatePath = emqx_mgmt_api_test_util:api_path(["api_key", Name]),
case emqx_mgmt_api_test_util:request_api(put, UpdatePath, "", AuthHeader, Change) of
{ok, Update} -> {ok, emqx_json:decode(Update, [return_maps])};

View File

@ -24,14 +24,19 @@ init_suite() ->
init_suite([]).
init_suite(Apps) ->
init_suite(Apps, fun set_special_configs/1).
init_suite(Apps, SetConfigs) ->
mria:start(),
application:load(emqx_management),
emqx_common_test_helpers:start_apps(Apps ++ [emqx_dashboard], fun set_special_configs/1).
emqx_common_test_helpers:start_apps(Apps ++ [emqx_dashboard], SetConfigs),
emqx_common_test_http:create_default_app().
end_suite() ->
end_suite([]).
end_suite(Apps) ->
emqx_common_test_http:delete_default_app(),
application:unload(emqx_management),
emqx_common_test_helpers:stop_apps(Apps ++ [emqx_dashboard]),
emqx_config:delete_override_conf_files(),
@ -43,8 +48,23 @@ set_special_configs(emqx_dashboard) ->
set_special_configs(_App) ->
ok.
%% there is no difference between the 'request' and 'request_api'
%% the 'request' is only to be compatible with the 'emqx_dashboard_api_test_helpers:request'
request(Method, Url) ->
request(Method, Url, []).
request(Method, Url, Body) ->
request_api_with_body(Method, Url, Body).
uri(Parts) ->
emqx_dashboard_api_test_helpers:uri(Parts).
%% compatible_mode will return as same as 'emqx_dashboard_api_test_helpers:request'
request_api_with_body(Method, Url, Body) ->
request_api(Method, Url, [], auth_header_(), Body, #{compatible_mode => true}).
request_api(Method, Url) ->
request_api(Method, Url, [], [], [], #{}).
request_api(Method, Url, auth_header_()).
request_api(Method, Url, AuthOrHeaders) ->
request_api(Method, Url, [], AuthOrHeaders, [], #{}).
@ -90,10 +110,20 @@ request_api(Method, Url, QueryParams, AuthOrHeaders, Body, Opts) when
do_request_api(Method, Request, Opts) ->
ReturnAll = maps:get(return_all, Opts, false),
CompatibleMode = maps:get(compatible_mode, Opts, false),
ReqOpts =
case CompatibleMode of
true ->
[{body_format, binary}];
_ ->
[]
end,
ct:pal("Method: ~p, Request: ~p", [Method, Request]),
case httpc:request(Method, Request, [], []) of
case httpc:request(Method, Request, [], ReqOpts) of
{error, socket_closed_remotely} ->
{error, socket_closed_remotely};
{ok, {{_, Code, _}, _Headers, Body}} when CompatibleMode ->
{ok, Code, Body};
{ok, {{"HTTP/1.1", Code, _} = Reason, Headers, Body}} when
Code >= 200 andalso Code =< 299 andalso ReturnAll
->
@ -109,10 +139,7 @@ do_request_api(Method, Request, Opts) ->
end.
auth_header_() ->
Username = <<"admin">>,
Password = <<"public">>,
{ok, Token} = emqx_dashboard_admin:sign_token(Username, Password),
{"Authorization", "Bearer " ++ binary_to_list(Token)}.
emqx_common_test_http:default_auth_header().
build_http_header(X) when is_list(X) ->
X;

View File

@ -30,6 +30,8 @@
-define(API_VERSION, "v5").
-define(BASE_PATH, "api").
-import(emqx_dashboard_SUITE, [auth_header_/0]).
%%--------------------------------------------------------------------
%% Setups
%%--------------------------------------------------------------------
@ -330,13 +332,6 @@ t_stream_log(_Config) ->
to_rfc3339(Second) ->
list_to_binary(calendar:system_time_to_rfc3339(Second)).
auth_header_() ->
auth_header_("admin", "public").
auth_header_(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User, ":", Pass])),
{"Authorization", "Basic " ++ Encoded}.
request_api(Method, Url, Auth) -> do_request_api(Method, {Url, [Auth]}).
request_api(Method, Url, Auth, Body) ->

View File

@ -26,7 +26,7 @@
<<"max_delayed_messages">> => <<"0">>
}).
-import(emqx_dashboard_api_test_helpers, [request/2, request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/2, request/3, uri/1]).
all() ->
emqx_common_test_helpers:all(?MODULE).
@ -36,27 +36,21 @@ init_per_suite(Config) ->
raw_with_default => true
}),
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_modules, emqx_dashboard],
fun set_special_configs/1
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_modules]
),
emqx_delayed:load(),
Config.
end_per_suite(Config) ->
ok = emqx_delayed:unload(),
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_dashboard, emqx_modules]),
emqx_mgmt_api_test_util:end_suite([emqx_conf, emqx_modules]),
Config.
init_per_testcase(_, Config) ->
{ok, _} = emqx_cluster_rpc:start_link(),
Config.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config();
set_special_configs(_App) ->
ok.
%%------------------------------------------------------------------------------
%% Test Cases
%%------------------------------------------------------------------------------

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -37,20 +37,14 @@ init_per_suite(Config) ->
raw_with_default => true
}),
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_modules, emqx_dashboard],
fun set_special_configs/1
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_modules]
),
Config.
end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_dashboard, emqx_modules]),
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config();
set_special_configs(_App) ->
emqx_mgmt_api_test_util:end_suite([emqx_conf, emqx_modules]),
ok.
%%------------------------------------------------------------------------------
@ -81,7 +75,7 @@ t_mqtt_topic_rewrite(_) ->
?assertEqual(
Rules,
jsx:decode(Result)
emqx_json:decode(Result, [return_maps])
).
t_mqtt_topic_rewrite_limit(_) ->

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/2, request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/2, request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -33,8 +33,8 @@ init_per_suite(Config) ->
raw_with_default => true
}),
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authn, emqx_authz, emqx_modules, emqx_dashboard],
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_authn, emqx_authz, emqx_modules],
fun set_special_configs/1
),
@ -49,8 +49,8 @@ end_per_suite(_Config) ->
<<"sources">> => []
}
),
emqx_common_test_helpers:stop_apps([
emqx_dashboard, emqx_conf, emqx_authn, emqx_authz, emqx_modules
emqx_mgmt_api_test_util:end_suite([
emqx_conf, emqx_authn, emqx_authz, emqx_modules
]),
ok.

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all).
-compile(export_all).
-import(emqx_dashboard_api_test_helpers, [request/3, uri/1]).
-import(emqx_mgmt_api_test_util, [request/2, request/3, uri/1]).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
@ -44,9 +44,8 @@ init_per_suite(Config) ->
raw_with_default => true
}),
ok = emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_modules, emqx_dashboard],
fun set_special_configs/1
ok = emqx_mgmt_api_test_util:init_suite(
[emqx_conf, emqx_modules]
),
%% When many tests run in an obscure order, it may occur that
@ -59,15 +58,10 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_dashboard, emqx_modules]),
emqx_mgmt_api_test_util:end_suite([emqx_conf, emqx_modules]),
application:stop(gen_rpc),
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config();
set_special_configs(_App) ->
ok.
%%------------------------------------------------------------------------------
%% Tests
%%------------------------------------------------------------------------------
@ -315,6 +309,3 @@ t_badrpc(_) ->
%%------------------------------------------------------------------------------
%% Helpers
%%------------------------------------------------------------------------------
request(Method, Url) ->
request(Method, Url, []).

View File

@ -203,13 +203,7 @@ do_request_api(Method, Request) ->
end.
auth_header_() ->
AppId = <<"admin">>,
AppSecret = <<"public">>,
auth_header_(binary_to_list(AppId), binary_to_list(AppSecret)).
auth_header_(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User, ":", Pass])),
{"Authorization", "Basic " ++ Encoded}.
emqx_mgmt_api_test_util:auth_header_().
api_path(Parts) ->
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts).

View File

@ -0,0 +1 @@
Basic auth is no longer allowed for API calls, must use API key instead.

View File

@ -0,0 +1 @@
API 调用不再支持基于 `username:password``baisc` 认证, 现在 API 必须通过 API Key 才能进行调用。