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"; echo "waiting emqx started";
sleep 10; sleep 10;
done 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 - name: Check cluster
timeout-minutes: 10 timeout-minutes: 10
run: | run: |
kubectl port-forward service/${{ matrix.profile }} 18083:18083 > /dev/null &
while 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 do
echo "waiting ${{ matrix.profile }} cluster scale" echo "waiting ${{ matrix.profile }} cluster scale"
sleep 1 sleep 1

View File

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

View File

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

View File

@ -29,6 +29,9 @@
auth_header/2 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) ->
request_api(Method, Url, [], Auth, []). request_api(Method, Url, [], Auth, []).
@ -74,12 +77,18 @@ auth_header(User, Pass) ->
{"Authorization", "Basic " ++ Encoded}. {"Authorization", "Basic " ++ Encoded}.
default_auth_header() -> default_auth_header() ->
AppId = <<"myappid">>, {ok, #{api_key := APIKey}} = emqx_mgmt_auth:read(?DEFAULT_APP_ID),
AppSecret = emqx_mgmt_auth:get_appsecret(AppId), auth_header(
auth_header(erlang:binary_to_list(AppId), erlang:binary_to_list(AppSecret)). erlang:binary_to_list(APIKey), erlang:binary_to_list(?DEFAULT_APP_SECRET)
).
create_default_app() -> 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() -> 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(nowarn_export_all).
-compile(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("emqx_authn.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -65,9 +66,8 @@ end_per_testcase(_, Config) ->
init_per_suite(Config) -> init_per_suite(Config) ->
emqx_config:erase(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY), emqx_config:erase(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY),
_ = application:load(emqx_conf), _ = application:load(emqx_conf),
ok = emqx_common_test_helpers:start_apps( ok = emqx_mgmt_api_test_util:init_suite(
[emqx_authn, emqx_dashboard], [emqx_authn]
fun set_special_configs/1
), ),
?AUTHN:delete_chain(?GLOBAL), ?AUTHN:delete_chain(?GLOBAL),
@ -76,12 +76,7 @@ init_per_suite(Config) ->
Config. Config.
end_per_suite(_Config) -> end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authn]), emqx_mgmt_api_test_util:end_suite([emqx_authn]),
ok.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config();
set_special_configs(_App) ->
ok. ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all). -compile(nowarn_export_all).
-compile(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("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
@ -32,8 +32,8 @@ groups() ->
[]. [].
init_per_suite(Config) -> 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, emqx_management], [emqx_conf, emqx_authz],
fun set_special_configs/1 fun set_special_configs/1
), ),
Config. Config.
@ -47,7 +47,7 @@ end_per_suite(_Config) ->
<<"sources">> => [] <<"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. ok.
set_special_configs(emqx_dashboard) -> 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 = emqtt:publish(C, <<"a/b/c">>, <<"{\"x\":1,\"y\":1}">>, 0),
{ok, 200, Result3} = request(get, uri(["clients", "emqx0", "authorization", "cache"])), {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"])), request(delete, uri(["authorization", "cache"])),
{ok, 200, Result4} = request(get, uri(["clients", "emqx0", "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. ok.

View File

@ -22,7 +22,7 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.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() -> all() ->
emqx_common_test_helpers:all(?MODULE). emqx_common_test_helpers:all(?MODULE).
@ -31,8 +31,8 @@ groups() ->
[]. [].
init_per_suite(Config) -> 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], [emqx_conf, emqx_authz],
fun set_special_configs/1 fun set_special_configs/1
), ),
Config. Config.
@ -46,7 +46,7 @@ end_per_suite(_Config) ->
<<"sources">> => [] <<"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. ok.
set_special_configs(emqx_dashboard) -> set_special_configs(emqx_dashboard) ->

View File

@ -18,7 +18,7 @@
-compile(nowarn_export_all). -compile(nowarn_export_all).
-compile(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("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
@ -30,7 +30,7 @@ groups() ->
[]. [].
init_per_suite(Config) -> 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], [emqx_conf, emqx_authz, emqx_dashboard],
fun set_special_configs/1 fun set_special_configs/1
), ),
@ -46,7 +46,7 @@ end_per_suite(_Config) ->
} }
), ),
ok = stop_apps([emqx_resource]), 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. ok.
set_special_configs(emqx_dashboard) -> set_special_configs(emqx_dashboard) ->

View File

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

View File

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

View File

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

View File

@ -65,8 +65,12 @@ start_listeners(Listeners) ->
components => #{ components => #{
schemas => #{}, schemas => #{},
'securitySchemes' => #{ 'securitySchemes' => #{
'basicAuth' => #{type => http, scheme => basic}, 'basicAuth' => #{
'bearerAuth' => #{type => http, scheme => bearer} 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) -> authorize(Req) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of case cowboy_req:parse_header(<<"authorization">>, Req) of
{basic, Username, Password} -> {basic, Username, Password} ->
case emqx_dashboard_admin:check(Username, Password) of api_key_authorize(Req, Username, Password);
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;
{bearer, Token} -> {bearer, Token} ->
case emqx_dashboard_admin:verify_token(Token) of case emqx_dashboard_admin:verify_token(Token) of
ok -> ok ->
@ -269,3 +252,20 @@ i18n_file() ->
listeners() -> listeners() ->
emqx_conf:get([dashboard, 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(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(WRONG_TOKEN_OR_USERNAME, 'WRONG_TOKEN_OR_USERNAME').
-define(USER_NOT_FOUND, 'USER_NOT_FOUND'). -define(USER_NOT_FOUND, 'USER_NOT_FOUND').
-define(ERROR_PWD_NOT_MATCH, 'ERROR_PWD_NOT_MATCH'). -define(ERROR_PWD_NOT_MATCH, 'ERROR_PWD_NOT_MATCH').
@ -164,7 +164,7 @@ schema("/users/:username/change_pwd") ->
}. }.
response_schema(401) -> 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) -> response_schema(404) ->
emqx_dashboard_swagger:error_codes([?USER_NOT_FOUND], ?DESC(users_api404)). emqx_dashboard_swagger:error_codes([?USER_NOT_FOUND], ?DESC(users_api404)).
@ -223,7 +223,7 @@ login(post, #{body := Params}) ->
}}; }};
{error, R} -> {error, R} ->
?SLOG(info, #{msg => "Dashboard login failed", username => Username, reason => 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. end.
logout(_, #{ logout(_, #{

View File

@ -114,9 +114,9 @@ t_admin_delete_self_failed(_) ->
?assertEqual(1, length(Admins)), ?assertEqual(1, length(Admins)),
Header = auth_header_(<<"username1">>, <<"password">>), Header = auth_header_(<<"username1">>, <<"password">>),
{error, {_, 400, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header), {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}, 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). mnesia:clear_table(?ADMIN).
t_rest_api(_Config) -> t_rest_api(_Config) ->

View File

@ -25,43 +25,24 @@
-define(SERVER, "http://127.0.0.1:18083/api/v5"). -define(SERVER, "http://127.0.0.1:18083/api/v5").
-import(emqx_mgmt_api_test_util, [request/2]).
all() -> all() ->
emqx_common_test_helpers:all(?MODULE). emqx_common_test_helpers:all(?MODULE).
init_per_suite(Config) -> init_per_suite(Config) ->
mria:start(), mria:start(),
application:load(emqx_dashboard), emqx_mgmt_api_test_util:init_suite([emqx_conf]),
emqx_common_test_helpers:start_apps([emqx_conf, emqx_dashboard], fun set_special_configs/1),
Config. Config.
set_special_configs(emqx_dashboard) ->
emqx_dashboard_api_test_helpers:set_default_config(),
ok;
set_special_configs(_) ->
ok.
end_per_suite(Config) -> end_per_suite(Config) ->
end_suite(), end_suite(),
Config. Config.
end_suite() -> end_suite() ->
application:unload(emqx_management), emqx_mgmt_api_test_util:end_suite([emqx_conf]).
emqx_common_test_helpers:stop_apps([emqx_dashboard]).
t_bad_api_path(_) -> t_bad_api_path(_) ->
Url = ?SERVER ++ "/for/test/some/path/not/exist", Url = ?SERVER ++ "/for/test/some/path/not/exist",
{error, {"HTTP/1.1", 404, "Not Found"}} = request(Url), {ok, 404, _} = request(get, Url),
ok. 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(nowarn_export_all).
-compile(export_all). -compile(export_all).
-import(emqx_dashboard_SUITE, [auth_header_/0]).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-include_lib("emqx/include/emqx.hrl"). -include_lib("emqx/include/emqx.hrl").
@ -153,10 +155,6 @@ do_request_api(Method, Request) ->
{error, Reason} {error, Reason}
end. end.
auth_header_() ->
Basic = binary_to_list(base64:encode(<<"admin:public">>)),
{"Authorization", "Basic " ++ Basic}.
restart_monitor() -> restart_monitor() ->
OldMonitor = erlang:whereis(emqx_dashboard_monitor), OldMonitor = erlang:whereis(emqx_dashboard_monitor),
erlang:exit(OldMonitor, kill), erlang:exit(OldMonitor, kill),

View File

@ -347,13 +347,7 @@ do_request_api(Method, Request) ->
end. end.
auth_header_() -> auth_header_() ->
AppId = <<"admin">>, emqx_mgmt_api_test_util:auth_header_().
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}.
api_path(Parts) -> api_path(Parts) ->
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts). ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts).

View File

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

View File

@ -40,6 +40,10 @@
do_force_create_app/3 do_force_create_app/3
]). ]).
-ifdef(TEST).
-export([create/5]).
-endif.
-define(APP, emqx_app). -define(APP, emqx_app).
-record(?APP, { -record(?APP, {
@ -68,8 +72,12 @@ init_bootstrap_file() ->
init_bootstrap_file(File). init_bootstrap_file(File).
create(Name, Enable, ExpiredAt, Desc) -> 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 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"} false -> {error, "Maximum ApiKey"}
end. 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(undefined) -> false;
is_expired(ExpiredTime) -> ExpiredTime < erlang:system_time(second). is_expired(ExpiredTime) -> ExpiredTime < erlang:system_time(second).
create_app(Name, Enable, ExpiredAt, Desc) -> create_app(Name, ApiSecret, Enable, ExpiredAt, Desc) ->
ApiSecret = generate_api_secret(),
App = App =
#?APP{ #?APP{
name = Name, name = Name,
@ -170,9 +177,10 @@ create_app(Name, Enable, ExpiredAt, Desc) ->
api_key = list_to_binary(emqx_misc:gen_id(16)) api_key = list_to_binary(emqx_misc:gen_id(16))
}, },
case create_app(App) of case create_app(App) of
{error, api_key_already_existed} -> create_app(Name, Enable, ExpiredAt, Desc); {ok, Res} ->
{ok, Res} -> {ok, Res#{api_secret => ApiSecret}}; {ok, Res#{api_secret => ApiSecret}};
Error -> Error Error ->
Error
end. end.
create_app(App = #?APP{api_key = ApiKey, name = Name}) -> create_app(App = #?APP{api_key = ApiKey, name = Name}) ->

View File

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

View File

@ -24,14 +24,19 @@ init_suite() ->
init_suite([]). init_suite([]).
init_suite(Apps) -> init_suite(Apps) ->
init_suite(Apps, fun set_special_configs/1).
init_suite(Apps, SetConfigs) ->
mria:start(), mria:start(),
application:load(emqx_management), 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([]). end_suite([]).
end_suite(Apps) -> end_suite(Apps) ->
emqx_common_test_http:delete_default_app(),
application:unload(emqx_management), application:unload(emqx_management),
emqx_common_test_helpers:stop_apps(Apps ++ [emqx_dashboard]), emqx_common_test_helpers:stop_apps(Apps ++ [emqx_dashboard]),
emqx_config:delete_override_conf_files(), emqx_config:delete_override_conf_files(),
@ -43,8 +48,23 @@ set_special_configs(emqx_dashboard) ->
set_special_configs(_App) -> set_special_configs(_App) ->
ok. 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, [], [], [], #{}). request_api(Method, Url, auth_header_()).
request_api(Method, Url, AuthOrHeaders) -> request_api(Method, Url, AuthOrHeaders) ->
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) -> do_request_api(Method, Request, Opts) ->
ReturnAll = maps:get(return_all, Opts, false), 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]), 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} ->
{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 {ok, {{"HTTP/1.1", Code, _} = Reason, Headers, Body}} when
Code >= 200 andalso Code =< 299 andalso ReturnAll Code >= 200 andalso Code =< 299 andalso ReturnAll
-> ->
@ -109,10 +139,7 @@ do_request_api(Method, Request, Opts) ->
end. end.
auth_header_() -> auth_header_() ->
Username = <<"admin">>, emqx_common_test_http:default_auth_header().
Password = <<"public">>,
{ok, Token} = emqx_dashboard_admin:sign_token(Username, Password),
{"Authorization", "Bearer " ++ binary_to_list(Token)}.
build_http_header(X) when is_list(X) -> build_http_header(X) when is_list(X) ->
X; X;

View File

@ -30,6 +30,8 @@
-define(API_VERSION, "v5"). -define(API_VERSION, "v5").
-define(BASE_PATH, "api"). -define(BASE_PATH, "api").
-import(emqx_dashboard_SUITE, [auth_header_/0]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Setups %% Setups
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -330,13 +332,6 @@ t_stream_log(_Config) ->
to_rfc3339(Second) -> to_rfc3339(Second) ->
list_to_binary(calendar:system_time_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) -> do_request_api(Method, {Url, [Auth]}).
request_api(Method, Url, Auth, Body) -> request_api(Method, Url, Auth, Body) ->

View File

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

View File

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

View File

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

View File

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

View File

@ -203,13 +203,7 @@ do_request_api(Method, Request) ->
end. end.
auth_header_() -> auth_header_() ->
AppId = <<"admin">>, emqx_mgmt_api_test_util:auth_header_().
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}.
api_path(Parts) -> api_path(Parts) ->
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ 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 才能进行调用。