%%-------------------------------------------------------------------- %% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %%-------------------------------------------------------------------- -module(emqx_mgmt_auth_api_SUITE). -compile(export_all). -compile(nowarn_export_all). -include_lib("eunit/include/eunit.hrl"). all() -> [{group, parallel}, {group, sequence}]. suite() -> [{timetrap, {minutes, 1}}]. groups() -> [ {parallel, [parallel], [t_create, t_update, t_delete, t_authorize, t_create_unexpired_app]}, {sequence, [], [t_create_failed]} ]. init_per_suite(Config) -> emqx_mgmt_api_test_util:init_suite(), Config. end_per_suite(_) -> emqx_mgmt_api_test_util:end_suite(). t_create(_Config) -> Name = <<"EMQX-API-KEY-1">>, {ok, Create} = create_app(Name), ?assertMatch(#{<<"api_key">> := _, <<"api_secret">> := _, <<"created_at">> := _, <<"desc">> := _, <<"enable">> := true, <<"expired_at">> := _, <<"name">> := Name}, Create), {ok, List} = list_app(), [App] = lists:filter(fun(#{<<"name">> := NameA}) -> NameA =:= Name end, List), ?assertEqual(false, maps:is_key(<<"api_secret">>, App)), {ok, App1} = read_app(Name), ?assertEqual(Name, maps:get(<<"name">>, App1)), ?assertEqual(true, maps:get(<<"enable">>, App1)), ?assertEqual(false, maps:is_key(<<"api_secret">>, App1)), ?assertEqual({error, {"HTTP/1.1", 404, "Not Found"}}, read_app(<<"EMQX-API-KEY-NO-EXIST">>)), ok. t_create_failed(_Config) -> BadRequest = {error, {"HTTP/1.1", 400, "Bad Request"}}, ?assertEqual(BadRequest, create_app(<<" error format name">>)), LongName = iolist_to_binary(lists:duplicate(257, "A")), ?assertEqual(BadRequest, create_app(<<" error format name">>)), ?assertEqual(BadRequest, create_app(LongName)), {ok, List} = list_app(), CreateNum = 30 - erlang:length(List), Names = lists:map(fun(Seq) -> <<"EMQX-API-FAILED-KEY-", (integer_to_binary(Seq))/binary>> end, lists:seq(1, CreateNum)), lists:foreach(fun(N) -> {ok, _} = create_app(N) end, Names), ?assertEqual(BadRequest, create_app(<<"EMQX-API-KEY-MAXIMUM">>)), lists:foreach(fun(N) -> {ok, _} = delete_app(N) end, Names), Name = <<"EMQX-API-FAILED-KEY-1">>, ?assertMatch({ok, _}, create_app(Name)), ?assertEqual(BadRequest, create_app(Name)), {ok, _} = delete_app(Name), ?assertMatch({ok, #{<<"name">> := Name}}, create_app(Name)), {ok, _} = delete_app(Name), ok. t_update(_Config) -> Name = <<"EMQX-API-UPDATE-KEY">>, {ok, _} = create_app(Name), ExpiredAt = to_rfc3339(erlang:system_time(second) + 10000), Change = #{ expired_at => ExpiredAt, desc => <<"NoteVersion1"/utf8>>, enable => false }, {ok, Update1} = update_app(Name, Change), ?assertEqual(Name, maps:get(<<"name">>, Update1)), ?assertEqual(false, maps:get(<<"enable">>, Update1)), ?assertEqual(<<"NoteVersion1"/utf8>>, maps:get(<<"desc">>, Update1)), ?assertEqual(calendar:rfc3339_to_system_time(binary_to_list(ExpiredAt)), calendar:rfc3339_to_system_time(binary_to_list(maps:get(<<"expired_at">>, Update1))) ), Unexpired1 = maps:without([expired_at], Change), {ok, Update2} = update_app(Name, Unexpired1), ?assertEqual(<<"undefined">>, maps:get(<<"expired_at">>, Update2)), Unexpired2 = Change#{expired_at => <<"undefined">>}, {ok, Update3} = update_app(Name, Unexpired2), ?assertEqual(<<"undefined">>, maps:get(<<"expired_at">>, Update3)), ?assertEqual({error, {"HTTP/1.1", 404, "Not Found"}}, update_app(<<"Not-Exist">>, Change)), ok. t_delete(_Config) -> Name = <<"EMQX-API-DELETE-KEY">>, {ok, _Create} = create_app(Name), {ok, Delete} = delete_app(Name), ?assertEqual([], Delete), ?assertEqual({error, {"HTTP/1.1", 404, "Not Found"}}, delete_app(Name)), ok. t_authorize(_Config) -> Name = <<"EMQX-API-AUTHORIZE-KEY">>, {ok, #{<<"api_key">> := ApiKey, <<"api_secret">> := ApiSecret}} = create_app(Name), BasicHeader = emqx_common_test_http:auth_header(binary_to_list(ApiKey), binary_to_list(ApiSecret)), SecretError = emqx_common_test_http:auth_header(binary_to_list(ApiKey), binary_to_list(ApiKey)), KeyError = emqx_common_test_http:auth_header("not_found_key", binary_to_list(ApiSecret)), Unauthorized = {error, {"HTTP/1.1", 401, "Unauthorized"}}, BanPath = emqx_mgmt_api_test_util:api_path(["banned"]), ApiKeyPath = emqx_mgmt_api_test_util:api_path(["api_key"]), UserPath = emqx_mgmt_api_test_util:api_path(["users"]), {ok, _Status} = emqx_mgmt_api_test_util:request_api(get, BanPath, BasicHeader), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, BanPath, KeyError)), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, BanPath, SecretError)), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, ApiKeyPath, BasicHeader)), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, UserPath, BasicHeader)), ?assertMatch({ok, #{<<"api_key">> := _, <<"enable">> := false}}, update_app(Name, #{enable => false})), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, BanPath, BasicHeader)), Expired = #{ expired_at => to_rfc3339(erlang:system_time(second) - 1), enable => true }, ?assertMatch({ok, #{<<"api_key">> := _, <<"enable">> := true}}, update_app(Name, Expired)), ?assertEqual(Unauthorized, emqx_mgmt_api_test_util:request_api(get, BanPath, BasicHeader)), UnExpired = #{expired_at => undefined}, ?assertMatch({ok, #{<<"api_key">> := _, <<"expired_at">> := <<"undefined">>}}, update_app(Name, UnExpired)), {ok, _Status1} = emqx_mgmt_api_test_util:request_api(get, BanPath, BasicHeader), ok. t_create_unexpired_app(_Config) -> Name1 = <<"EMQX-UNEXPIRED-API-KEY-1">>, Name2 = <<"EMQX-UNEXPIRED-API-KEY-2">>, {ok, Create1} = create_unexpired_app(Name1, #{}), ?assertMatch(#{<<"expired_at">> := <<"undefined">>}, Create1), {ok, Create2} = create_unexpired_app(Name2, #{expired_at => <<"undefined">>}), ?assertMatch(#{<<"expired_at">> := <<"undefined">>}, Create2), ok. list_app() -> Path = emqx_mgmt_api_test_util:api_path(["api_key"]), case emqx_mgmt_api_test_util:request_api(get, Path) of {ok, Apps} -> {ok, emqx_json:decode(Apps, [return_maps])}; Error -> Error end. read_app(Name) -> Path = emqx_mgmt_api_test_util:api_path(["api_key", Name]), case emqx_mgmt_api_test_util:request_api(get, Path) of {ok, Res} -> {ok, emqx_json:decode(Res, [return_maps])}; Error -> Error end. create_app(Name) -> AuthHeader = emqx_mgmt_api_test_util:auth_header_(), Path = emqx_mgmt_api_test_util:api_path(["api_key"]), ExpiredAt = to_rfc3339(erlang:system_time(second) + 1000), App = #{ name => Name, expired_at => ExpiredAt, desc => <<"Note"/utf8>>, enable => true }, case emqx_mgmt_api_test_util:request_api(post, Path, "", AuthHeader, App) of {ok, Res} -> {ok, emqx_json:decode(Res, [return_maps])}; Error -> Error end. create_unexpired_app(Name, Params) -> AuthHeader = emqx_mgmt_api_test_util: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 {ok, Res} -> {ok, emqx_json:decode(Res, [return_maps])}; Error -> Error end. delete_app(Name) -> DeletePath = emqx_mgmt_api_test_util:api_path(["api_key", Name]), emqx_mgmt_api_test_util:request_api(delete, DeletePath). update_app(Name, Change) -> AuthHeader = emqx_mgmt_api_test_util: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])}; Error -> Error end. to_rfc3339(Sec) -> list_to_binary(calendar:system_time_to_rfc3339(Sec)).