diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 85b411217..a0bdb5150 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -38,7 +38,6 @@ -type ip_port() :: tuple(). -type cipher() :: map(). -type rfc3339_system_time() :: integer(). --type unicode_binary() :: binary(). -typerefl_from_string({duration/0, emqx_schema, to_duration}). -typerefl_from_string({duration_s/0, emqx_schema, to_duration_s}). @@ -52,7 +51,6 @@ -typerefl_from_string({cipher/0, emqx_schema, to_erl_cipher_suite}). -typerefl_from_string({comma_separated_atoms/0, emqx_schema, to_comma_separated_atoms}). -typerefl_from_string({rfc3339_system_time/0, emqx_schema, rfc3339_to_system_time}). --typerefl_from_string({unicode_binary/0, emqx_schema, to_unicode_binary}). -export([ validate_heap_size/1 , parse_user_lookup_fun/1 @@ -66,8 +64,7 @@ to_bar_separated_list/1, to_ip_port/1, to_erl_cipher_suite/1, to_comma_separated_atoms/1, - rfc3339_to_system_time/1, - to_unicode_binary/1]). + rfc3339_to_system_time/1]). -behaviour(hocon_schema). @@ -76,8 +73,7 @@ comma_separated_list/0, bar_separated_list/0, ip_port/0, cipher/0, comma_separated_atoms/0, - rfc3339_system_time/0, - unicode_binary/0]). + rfc3339_system_time/0]). -export([namespace/0, roots/0, roots/1, fields/1]). -export([conf_get/2, conf_get/3, keys/2, filter/1]). @@ -1390,9 +1386,6 @@ rfc3339_to_system_time(DateTime) -> {error, bad_rfc3339_timestamp} end. -to_unicode_binary(Str) -> - {ok, unicode:characters_to_binary(Str)}. - to_bar_separated_list(Str) -> {ok, string:tokens(Str, "| ")}. diff --git a/apps/emqx_dashboard/src/emqx_dashboard_api.erl b/apps/emqx_dashboard/src/emqx_dashboard_api.erl index 45a3b7c56..ae2eb4e42 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_api.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_api.erl @@ -123,7 +123,7 @@ schema("/users/:username") -> #{in => path, example => <<"admin">>})}], 'requestBody' => [ { description - , mk(emqx_schema:unicode_binary(), + , mk(binary(), #{desc => <<"User description">>, example => <<"administrator">>})} ], responses => #{ @@ -176,7 +176,7 @@ schema("/users/:username/change_pwd") -> fields(user) -> [ {description, - mk(emqx_schema:unicode_binary(), + mk(binary(), #{desc => <<"User description">>, example => "administrator"})}, {username, mk(binary(), diff --git a/apps/emqx_management/src/emqx_mgmt_api_app.erl b/apps/emqx_management/src/emqx_mgmt_api_app.erl index b77f1a214..dfce3cf30 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_app.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_app.erl @@ -91,16 +91,17 @@ fields(app) -> """They are useful for accessing public data anonymously,""" """and are used to associate API requests.""", example => <<"MzAyMjk3ODMwMDk0NjIzOTUxNjcwNzQ0NzQ3MTE2NDYyMDI">>})}, - {expired_at, hoconsc:mk(emqx_schema:rfc3339_system_time(), + {expired_at, hoconsc:mk(hoconsc:union([undefined, emqx_schema:rfc3339_system_time()]), #{desc => "No longer valid datetime", example => <<"2021-12-05T02:01:34.186Z">>, - nullable => true + nullable => true, + default => undefined })}, {created_at, hoconsc:mk(emqx_schema:rfc3339_system_time(), #{desc => "ApiKey create datetime", example => <<"2021-12-01T00:00:00.000Z">> })}, - {desc, hoconsc:mk(emqx_schema:unicode_binary(), + {desc, hoconsc:mk(binary(), #{example => <<"Note">>, nullable => true})}, {enable, hoconsc:mk(boolean(), #{desc => "Enable/Disable", nullable => true})} ]; @@ -136,9 +137,15 @@ api_key(post, #{body := App}) -> #{ <<"name">> := Name, <<"desc">> := Desc0, - <<"expired_at">> := ExpiredAt, <<"enable">> := Enable } = App, + %% undefined is never expired + ExpiredAt0 = maps:get(<<"expired_at">>, App, <<"undefined">>), + ExpiredAt = + case ExpiredAt0 of + <<"undefined">> -> undefined; + _ -> ExpiredAt0 + end, Desc = unicode:characters_to_binary(Desc0, unicode), case emqx_mgmt_auth:create(Name, Enable, ExpiredAt, Desc) of {ok, NewApp} -> {200, format(NewApp)}; @@ -164,8 +171,13 @@ api_key_by_name(put, #{bindings := #{name := Name}, body := Body}) -> {error, not_found} -> {404, <<"NOT_FOUND">>} end. -format(App = #{expired_at := ExpiredAt, created_at := CreateAt}) -> +format(App = #{expired_at := ExpiredAt0, created_at := CreateAt}) -> + ExpiredAt = + case ExpiredAt0 of + undefined -> <<"undefined">>; + _ -> list_to_binary(calendar:system_time_to_rfc3339(ExpiredAt0)) + end, App#{ - expired_at => list_to_binary(calendar:system_time_to_rfc3339(ExpiredAt)), + expired_at => ExpiredAt, created_at => list_to_binary(calendar:system_time_to_rfc3339(CreateAt)) }. diff --git a/apps/emqx_management/src/emqx_mgmt_api_banned.erl b/apps/emqx_management/src/emqx_mgmt_api_banned.erl index 911b298a1..c6ff56ad0 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_banned.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_banned.erl @@ -101,15 +101,15 @@ fields(ban) -> desc => <<"Banned type clientid, username, peerhost">>, nullable => false, example => username})}, - {who, hoconsc:mk(emqx_schema:unicode_binary(), #{ + {who, hoconsc:mk(binary(), #{ desc => <<"Client info as banned type">>, nullable => false, example => <<"Badass坏"/utf8>>})}, - {by, hoconsc:mk(emqx_schema:unicode_binary(), #{ + {by, hoconsc:mk(binary(), #{ desc => <<"Commander">>, nullable => true, example => <<"mgmt_api">>})}, - {reason, hoconsc:mk(emqx_schema:unicode_binary(), #{ + {reason, hoconsc:mk(binary(), #{ desc => <<"Banned reason">>, nullable => true, example => <<"Too many requests">>})}, diff --git a/apps/emqx_management/src/emqx_mgmt_auth.erl b/apps/emqx_management/src/emqx_mgmt_auth.erl index ae6b0820d..512ec6b0f 100644 --- a/apps/emqx_management/src/emqx_mgmt_auth.erl +++ b/apps/emqx_management/src/emqx_mgmt_auth.erl @@ -37,7 +37,7 @@ api_secret_hash = <<>> :: binary() | '_', enable = true :: boolean() | '_', desc = <<>> :: binary() | '_', - expired_at = 0 :: integer() | '_', + expired_at = 0 :: integer() | undefined | '_', created_at = 0 :: integer() | '_' }). diff --git a/apps/emqx_management/test/emqx_mgmt_auth_api_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_auth_api_SUITE.erl index 185ad5343..73d4ad566 100644 --- a/apps/emqx_management/test/emqx_mgmt_auth_api_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_auth_api_SUITE.erl @@ -23,7 +23,7 @@ all() -> [{group, parallel}, {group, sequence}]. suite() -> [{timetrap, {minutes, 1}}]. groups() -> [ - {parallel, [parallel], [t_create, t_update, t_delete, t_authorize]}, + {parallel, [parallel], [t_create, t_update, t_delete, t_authorize, t_create_unexpired_app]}, {sequence, [], [t_create_failed]} ]. @@ -137,7 +137,15 @@ t_authorize(_Config) -> }, ?assertMatch({ok, #{<<"api_key">> := _, <<"enable">> := true}}, update_app(Name, Expired)), ?assertEqual(Unauthorized, 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. @@ -170,6 +178,15 @@ create_app(Name) -> 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).