From f033fad7b37caaa5e7eb4ee4ecd0ab51ed460037 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Fri, 19 Nov 2021 11:20:48 +0800 Subject: [PATCH 1/5] refactor(gw): deps on emqx_dasboard_swagger --- apps/emqx_authn/src/emqx_authn_api.erl | 9 + .../src/emqx_gateway_api_authn.erl | 324 ++++++++--- .../src/emqx_gateway_api_listeners.erl | 513 ++++++++++-------- apps/emqx_gateway/src/emqx_gateway_http.erl | 29 +- .../src/emqx_gateway_insta_sup.erl | 22 +- apps/emqx_gateway/src/emqx_gateway_utils.erl | 19 + .../emqx_gateway/test/emqx_sn_frame_SUITE.erl | 2 +- 7 files changed, 597 insertions(+), 321 deletions(-) diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index bef27f99d..de3e0b5d3 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -65,6 +65,15 @@ , response_users_example/0 ]). +%% export these funcs for gateway +-export([ list_users/3 + , add_user/3 + , delete_user/3 + , find_user/3 + , update_user/4 + , serialize_error/1 + ]). + api_spec() -> emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). diff --git a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl index 4cd2d8867..e529bf2ae 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl @@ -18,21 +18,34 @@ -behaviour(minirest_api). +-include_lib("typerefl/include/types.hrl"). + +-define(BAD_REQUEST, 'BAD_REQUEST'). +-define(NOT_FOUND, 'NOT_FOUND'). +-define(INTERNAL_ERROR, 'INTERNAL_SERVER_ERROR'). + +-import(hoconsc, [mk/2, ref/2]). +-import(emqx_dashboard_swagger, [error_codes/2]). + -import(emqx_gateway_http, [ return_http_error/2 - , schema_bad_request/0 - , schema_not_found/0 - , schema_internal_error/0 - , schema_no_content/0 , with_gateway/2 + , with_authn/2 , checks/2 ]). -%% minirest behaviour callbacks --export([api_spec/0]). +%% minirest/dashbaord_swagger behaviour callbacks +-export([ api_spec/0 + , paths/0 + , schema/1 + ]). %% http handlers --export([authn/2]). +-export([ authn/2 + , users/2 + , users_insta/2 + , import_users/2 + ]). %% internal export for emqx_gateway_api_listeners module -export([schema_authn/0]). @@ -42,10 +55,13 @@ %%-------------------------------------------------------------------- api_spec() -> - {metadata(apis()), []}. + emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). -apis() -> - [ {"/gateway/:name/authentication", authn} +paths() -> + [ "/gateway/:name/authentication" + , "/gateway/:name/authentication/users" + , "/gateway/:name/authentication/users/:uid" + , "/gateway/:name/authentication/import_users" ]. %%-------------------------------------------------------------------- @@ -83,87 +99,245 @@ authn(delete, #{bindings := #{name := Name0}}) -> {204} end). +users(get, #{bindings := #{name := Name0}, query_string := Qs}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + emqx_authn_api:list_users(ChainName, AuthId, page_pramas(Qs)) + end); +users(post, #{bindings := #{name := Name0}, + body := Body}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + emqx_authn_api:add_user(ChainName, AuthId, Body) + end). + +users_insta(get, #{bindings := #{name := Name0, uid := UserId}}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + emqx_authn_api:find_user(ChainName, AuthId, UserId) + end); +users_insta(put, #{bindings := #{name := Name0, uid := UserId}, + body := Body}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + emqx_authn_api:update_user(ChainName, AuthId, UserId, Body) + end); +users_insta(delete, #{bindings := #{name := Name0, uid := UserId}}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + emqx_authn_api:delete_user(ChainName, AuthId, UserId) + end). + +import_users(post, #{bindings := #{name := Name0}, + body := Body}) -> + with_authn(Name0, fun(_GwName, #{id := AuthId, + chain_name := ChainName}) -> + case maps:get(<<"filename">>, Body, undefined) of + undefined -> + emqx_authn_api:serialize_error({missing_parameter, filename}); + Filename -> + case emqx_authentication:import_users( + ChainName, AuthId, Filename) of + ok -> {204}; + {error, Reason} -> + emqx_authn_api:serialize_error(Reason) + end + end + end). + +%%-------------------------------------------------------------------- +%% Utils + +page_pramas(Qs) -> + maps:with([<<"page">>, <<"limit">>], Qs). + %%-------------------------------------------------------------------- %% Swagger defines %%-------------------------------------------------------------------- -metadata(APIs) -> - metadata(APIs, []). -metadata([], APIAcc) -> - lists:reverse(APIAcc); -metadata([{Path, Fun}|More], APIAcc) -> - Methods = [get, post, put, delete, patch], - Mds = lists:foldl(fun(M, Acc) -> - try - Acc#{M => swagger(Path, M)} - catch - error : function_clause -> - Acc - end - end, #{}, Methods), - metadata(More, [{Path, Mds, Fun} | APIAcc]). -swagger("/gateway/:name/authentication", get) -> - #{ description => <<"Get the gateway authentication">> - , parameters => params_gateway_name_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_authn() - , <<"204">> => schema_no_content() - } +schema("/gateway/:name/authentication") -> + #{ 'operationId' => authn, + get => + #{ description => <<"Get the gateway authentication">> + , parameters => params_gateway_name_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => schema_authn() + , 204 => <<"Authentication does not initiated">> + } + }, + put => + #{ description => <<"Update authentication for the gateway">> + , parameters => params_gateway_name_in_path() + , requestBody => schema_authn() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Updated">> %% XXX: ??? return the updated object + } + }, + post => + #{ description => <<"Add authentication for the gateway">> + , parameters => params_gateway_name_in_path() + , requestBody => schema_authn() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Added">> + } + }, + delete => + #{ description => <<"Remove the gateway authentication">> + , parameters => params_gateway_name_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Deleted">> + } + } }; -swagger("/gateway/:name/authentication", put) -> - #{ description => <<"Update authentication for the gateway">> - , parameters => params_gateway_name_in_path() - , requestBody => schema_authn() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } +schema("/gateway/:name/authentication/users") -> + #{ 'operationId' => users + , get => + #{ description => <<"Get the users for the authentication">> + , parameters => params_gateway_name_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + post => + #{ description => <<"Add user for the authentication">> + , parameters => params_gateway_name_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 201 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + } }; -swagger("/gateway/:name/authentication", post) -> - #{ description => <<"Add authentication for the gateway">> - , parameters => params_gateway_name_in_path() - , requestBody => schema_authn() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } +schema("/gateway/:name/authentication/users/:uid") -> + #{ 'operationId' => users_insta + , get => + #{ description => <<"Get user info from the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_userid_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + put => + #{ description => <<"Update the user info for the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_userid_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + delete => + #{ description => <<"Delete the user for the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_userid_in_path() ++ + params_paging_in_qs() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + } }; -swagger("/gateway/:name/authentication", delete) -> - #{ description => <<"Remove the gateway authentication">> - , parameters => params_gateway_name_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } +schema("/gateway/:name/authentication/import_users") -> + #{ 'operationId' => import_users + , post => + #{ description => <<"Import users into the gateway authentication">> + , parameters => params_gateway_name_in_path() + , requestBody => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_import_users), + emqx_authn_api:request_import_users_examples() + ) + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + %% XXX: Put a hint message into 204 return ? + , 204 => <<"Imported">> + } + } }. %%-------------------------------------------------------------------- %% params defines params_gateway_name_in_path() -> - [#{ name => name - , in => path - , schema => #{type => string} - , required => true - }]. + [{name, + mk(binary(), + #{ in => path + , desc => <<"Gateway Name">> + })} + ]. + +params_userid_in_path() -> + [{uid, mk(binary(), + #{ in => path + , desc => <<"User ID">> + })} + ]. + +params_paging_in_qs() -> + [{page, mk(integer(), + #{ in => query + , desc => <<"Page Number">> + })}, + {limit, mk(integer(), + #{ in => query + , desc => <<"Page Limit">> + })} + ]. %%-------------------------------------------------------------------- %% schemas schema_authn() -> - #{ description => <<"OK">> - , content => #{ - 'application/json' => #{ - schema => minirest:ref(<<"AuthenticatorInstance">>) - }} - }. + emqx_dashboard_swagger:schema_with_examples( + emqx_authn_schema:authenticator_type(), + emqx_authn_api:authenticator_examples() + ). diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index 0ab054df8..25bfc5b5c 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -18,20 +18,32 @@ -behaviour(minirest_api). +-include_lib("typerefl/include/types.hrl"). + +-define(BAD_REQUEST, 'BAD_REQUEST'). +-define(NOT_FOUND, 'NOT_FOUND'). +-define(INTERNAL_ERROR, 'INTERNAL_SERVER_ERROR'). + +-import(hoconsc, [mk/2, ref/1, ref/2]). +-import(emqx_dashboard_swagger, [error_codes/2]). + -import(emqx_gateway_http, [ return_http_error/2 - , schema_bad_request/0 - , schema_not_found/0 - , schema_internal_error/0 - , schema_no_content/0 , with_gateway/2 , checks/2 ]). -import(emqx_gateway_api_authn, [schema_authn/0]). -%% minirest behaviour callbacks --export([api_spec/0]). +%% minirest/dashbaord_swagger behaviour callbacks +-export([ api_spec/0 + , paths/0 + , schema/1 + ]). + +-export([ roots/0 + , fields/1 + ]). %% http handlers -export([ listeners/2 @@ -44,12 +56,12 @@ %%-------------------------------------------------------------------- api_spec() -> - {metadata(apis()), []}. + emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). -apis() -> - [ {"/gateway/:name/listeners", listeners} - , {"/gateway/:name/listeners/:id", listeners_insta} - , {"/gateway/:name/listeners/:id/authentication", listeners_insta_authn} +paths() -> + [ "/gateway/:name/listeners" + , "/gateway/:name/listeners/:id" + , "/gateway/:name/listeners/:id/authentication" ]. %%-------------------------------------------------------------------- @@ -149,219 +161,228 @@ listeners_insta_authn(delete, #{bindings := #{name := Name0, %% Swagger defines %%-------------------------------------------------------------------- -metadata(APIs) -> - metadata(APIs, []). -metadata([], APIAcc) -> - lists:reverse(APIAcc); -metadata([{Path, Fun}|More], APIAcc) -> - Methods = [get, post, put, delete, patch], - Mds = lists:foldl(fun(M, Acc) -> - try - Acc#{M => swagger(Path, M)} - catch - error : function_clause -> - Acc - end - end, #{}, Methods), - metadata(More, [{Path, Mds, Fun} | APIAcc]). - -swagger("/gateway/:name/listeners", get) -> - #{ description => <<"Get the gateway listeners">> - , parameters => params_gateway_name_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_listener_list() - } +schema("/gateway/:name/listeners") -> + #{ 'operationId' => listeners, + get => + #{ description => <<"Get the gateway listeners">> + , parameters => params_gateway_name_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_examples( + hoconsc:array(ref(listener)), + examples_listener_list()) + } + }, + post => + #{ description => <<"Create the gateway listener">> + , parameters => params_gateway_name_in_path() + , requestBody => emqx_dashboard_swagger:schema_with_examples( + ref(listener), + examples_listener()) + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Created">> + } + } }; -swagger("/gateway/:name/listeners", post) -> - #{ description => <<"Create the gateway listener">> - , parameters => params_gateway_name_in_path() - , requestBody => schema_listener() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_listener_list() - } +schema("/gateway/:name/listeners/:id") -> + #{ 'operationId' => listeners_insta, + get => + #{ description => <<"Get the gateway listener configurations">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_examples( + ref(listener), + examples_listener()) + } + }, + delete => + #{ description => <<"Delete the gateway listener">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Deleted">> + } + }, + put => + #{ description => <<"Update the gateway listener">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , requestBody => emqx_dashboard_swagger:schema_with_examples( + ref(listener), + examples_listener()) + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => <<"Updated">> + } + } }; -swagger("/gateway/:name/listeners/:id", get) -> - #{ description => <<"Get the gateway listener configurations">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_listener() - } - }; -swagger("/gateway/:name/listeners/:id", delete) -> - #{ description => <<"Delete the gateway listener">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } - }; -swagger("/gateway/:name/listeners/:id", put) -> - #{ description => <<"Update the gateway listener">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , requestBody => schema_listener() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_no_content() - } - }; -swagger("/gateway/:name/listeners/:id/authentication", get) -> - #{ description => <<"Get the listener's authentication info">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"200">> => schema_authn() - , <<"204">> => schema_no_content() - } - }; -swagger("/gateway/:name/listeners/:id/authentication", post) -> - #{ description => <<"Add authentication for the listener">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , requestBody => schema_authn() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } - }; -swagger("/gateway/:name/listeners/:id/authentication", put) -> - #{ description => <<"Update authentication for the listener">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , requestBody => schema_authn() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } - }; -swagger("/gateway/:name/listeners/:id/authentication", delete) -> - #{ description => <<"Remove authentication for the listener">> - , parameters => params_gateway_name_in_path() - ++ params_listener_id_in_path() - , responses => - #{ <<"400">> => schema_bad_request() - , <<"404">> => schema_not_found() - , <<"500">> => schema_internal_error() - , <<"204">> => schema_no_content() - } +schema("/gateway/:name/listeners/:id/authentication") -> + #{ 'operationId' => listeners_insta_authn, + get => + #{ description => <<"Get the listener's authentication info">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => schema_authn() + , 204 => <<"Authentication does not initiated">> + } + }, + post => + #{ description => <<"Add authentication for the listener">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , requestBody => schema_authn() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Added">> + } + }, + put => + #{ description => <<"Update authentication for the listener">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , requestBody => schema_authn() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Updated">> + } + }, + delete => + #{ description => <<"Remove authentication for the listener">> + , parameters => params_gateway_name_in_path() + ++ params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 204 => <<"Deleted">> + } + } }. %%-------------------------------------------------------------------- %% params defines params_gateway_name_in_path() -> - [#{ name => name - , in => path - , schema => #{type => string} - , required => true - }]. + [{name, + mk(binary(), + #{ in => path + , desc => <<"Gateway Name">> + })} + ]. params_listener_id_in_path() -> - [#{ name => id - , in => path - , schema => #{type => string} - , required => true - }]. + [{id, + mk(binary(), + #{ in => path + , desc => <<"Listener ID">> + })} + ]. %%-------------------------------------------------------------------- %% schemas -schema_listener_list() -> - emqx_mgmt_util:array_schema( - #{ type => object - , properties => properties_listener() - }, - <<"Listener list">> - ). - -schema_listener() -> - emqx_mgmt_util:schema( - #{ type => object - , properties => properties_listener() - } - ). - -%%-------------------------------------------------------------------- -%% properties - -properties_listener() -> - emqx_mgmt_util:properties( - raw_properties_common_listener() ++ - [ {tcp, object, raw_properties_tcp_opts()} - , {ssl, object, raw_properties_ssl_opts()} - , {udp, object, raw_properties_udp_opts()} - , {dtls, object, raw_properties_dtls_opts()} - ]). - -raw_properties_tcp_opts() -> - [ {active_n, integer, <<>>} - , {backlog, integer, <<>>} - , {buffer, string, <<>>} - , {recbuf, string, <<>>} - , {sndbuf, string, <<>>} - , {high_watermark, string, <<>>} - , {nodelay, boolean, <<>>} - , {reuseaddr, boolean, <<>>} - , {send_timeout, string, <<>>} - , {send_timeout_close, boolean, <<>>} +roots() -> + [ listener ]. -raw_properties_ssl_opts() -> - [ {cacertfile, string, <<>>} - , {certfile, string, <<>>} - , {keyfile, string, <<>>} - , {verify, string, <<>>} - , {fail_if_no_peer_cert, boolean, <<>>} - , {server_name_indication, boolean, <<>>} - , {depth, integer, <<>>} - , {password, string, <<>>} - , {handshake_timeout, string, <<>>} - , {versions, {array, string}, <<>>} - , {ciphers, {array, string}, <<>>} - , {user_lookup_fun, string, <<>>} - , {reuse_sessions, boolean, <<>>} - , {secure_renegotiate, boolean, <<>>} - , {honor_cipher_order, boolean, <<>>} - , {dhfile, string, <<>>} - ]. - -raw_properties_udp_opts() -> - [ {active_n, integer, <<>>} - , {buffer, string, <<>>} - , {recbuf, string, <<>>} - , {sndbuf, string, <<>>} - , {reuseaddr, boolean, <<>>} - ]. - -raw_properties_dtls_opts() -> +fields(listener) -> + common_listener_opts() ++ + [ {tcp, + mk(ref(tcp_listener_opts), + #{ nullable => {true, recursively} + , desc => <<"The tcp socket options for tcp or ssl listener">> + })} + , {ssl, + mk(ref(ssl_listener_opts), + #{ nullable => {true, recursively} + , desc => <<"The ssl socket options for ssl listener">> + })} + , {udp, + mk(ref(udp_listener_opts), + #{ nullable => {true, recursively} + , desc => <<"The udp socket options for udp or dtls listener">> + })} + , {dtls, + mk(ref(dtls_listener_opts), + #{ nullable => {true, recursively} + , desc => <<"The dtls socket options for dtls listener">> + })} + ]; +fields(tcp_listener_opts) -> + [ {active_n, mk(integer(), #{})} + , {backlog, mk(integer(), #{})} + , {buffer, mk(binary(), #{})} + , {recbuf, mk(binary(), #{})} + , {sndbuf, mk(binary(), #{})} + , {high_watermark, mk(binary(), #{})} + , {nodelay, mk(boolean(), #{})} + , {reuseaddr, boolean()} + , {send_timeout, binary()} + , {send_timeout_close, boolean()} + ]; +fields(ssl_listener_opts) -> + [ {cacertfile, binary()} + , {certfile, binary()} + , {keyfile, binary()} + , {verify, binary()} + , {fail_if_no_peer_cert, boolean()} + , {server_name_indication, boolean()} + , {depth, integer()} + , {password, binary()} + , {handshake_timeout, binary()} + , {versions, hoconsc:array(binary())} + , {ciphers, hoconsc:array(binary())} + , {user_lookup_fun, binary()} + , {reuse_sessions, boolean()} + , {secure_renegotiate, boolean()} + , {honor_cipher_order, boolean()} + , {dhfile, binary()} + ]; +fields(udp_listener_opts) -> + [ {active_n, integer()} + , {buffer, binary()} + , {recbuf, binary()} + , {sndbuf, binary()} + , {reuseaddr, boolean()} + ]; +fields(dtls_listener_opts) -> Ls = lists_key_without( [versions,ciphers,handshake_timeout], 1, - raw_properties_ssl_opts() + fields(ssl_listener_opts) ), - [ {versions, {array, string}, <<>>} - , {ciphers, {array, string}, <<>>} + [ {versions, hoconsc:array(binary())} + , {ciphers, hoconsc:array(binary())} | Ls]. lists_key_without([], _N, L) -> @@ -369,23 +390,67 @@ lists_key_without([], _N, L) -> lists_key_without([K|Ks], N, L) -> lists_key_without(Ks, N, lists:keydelete(K, N, L)). -raw_properties_common_listener() -> - [ {enable, boolean, <<"Whether to enable this listener">>} - , {id, string, <<"Listener Id">>} - , {name, string, <<"Listener name">>} - , {type, string, - <<"Listener type. Enum: tcp, udp, ssl, dtls">>, - [<<"tcp">>, <<"ssl">>, <<"udp">>, <<"dtls">>]} - , {running, boolean, <<"Listener running status">>} - , {bind, string, <<"Listener bind address or port">>} - , {acceptors, integer, <<"Listener acceptors number">>} - , {access_rules, {array, string}, <<"Listener Access rules for client">>} - , {max_conn_rate, integer, <<"Max connection rate for the listener">>} - , {max_connections, integer, <<"Max connections for the listener">>} - , {mountpoint, string, - <<"The Mounpoint for clients of the listener. " - "The gateway-level mountpoint configuration can be overloaded " - "when it is not null or empty string">>} +common_listener_opts() -> + [ {enable, + mk(boolean(), + #{ nullable => true + , desc => <<"Whether to enable this listener">>})} + , {id, + mk(binary(), + #{ nullable => true + , desc => <<"Listener Id">>})} + , {name, + mk(binary(), + #{ nullable => true + , desc => <<"Listener name">>})} + , {type, + mk(hoconsc:enum([tcp, ssl, udp, dtls]), + #{ nullable => true + , desc => <<"Listener type. Enum: tcp, udp, ssl, dtls">>})} + , {running, + mk(boolean(), + #{ nullable => true + , desc => <<"Listener running status">>})} + , {bind, + mk(binary(), + #{ nullable => true + , desc => <<"Listener bind address or port">>})} + , {acceptors, + mk(integer(), + #{ nullable => true + , desc => <<"Listener acceptors number">>})} + , {access_rules, + mk(hoconsc:array(binary()), + #{ nullable => true + , desc => <<"Listener Access rules for client">>})} + , {max_conn_rate, + mk(integer(), + #{ nullable => true + , desc => <<"Max connection rate for the listener">>})} + , {max_connections, + mk(integer(), + #{ nullable => true + , desc => <<"Max connections for the listener">>})} + , {mountpoint, + mk(binary(), + #{ nullable => true + , desc => +<<"The Mounpoint for clients of the listener. " + "The gateway-level mountpoint configuration can be overloaded " + "when it is not null or empty string">>})} %% FIXME: - , {authentication, string, <<"NOT-SUPPORTED-NOW">>} - ]. + , {authentication, + mk(emqx_authn_schema:authenticator_type(), + #{ nullable => {true, recursively} + , desc => <<"The authenticatior for this listener">> + })} + ]. + +%%-------------------------------------------------------------------- +%% examples + +examples_listener_list() -> + [examples_listener()]. + +examples_listener() -> + #{id => true}. diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index da440dba8..41923a66e 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -53,6 +53,7 @@ %% Utils for http, swagger, etc. -export([ return_http_error/2 , with_gateway/2 + , with_authn/2 , checks/2 , schema_bad_request/0 , schema_not_found/0 @@ -159,14 +160,31 @@ remove_listener(ListenerId) -> -spec authn(gateway_name()) -> map(). authn(GwName) -> + %% XXX: Need append chain-nanme, authenticator-id? Path = [gateway, GwName, authentication], - emqx_map_lib:jsonable_map(emqx:get_config(Path)). + ChainName = emqx_gateway_utils:global_chain(GwName), + wrap_chain_name( + ChainName, + emqx_map_lib:jsonable_map(emqx:get_config(Path)) + ). -spec authn(gateway_name(), binary()) -> map(). authn(GwName, ListenerId) -> {_, Type, Name} = emqx_gateway_utils:parse_listener_id(ListenerId), Path = [gateway, GwName, listeners, Type, Name, authentication], - emqx_map_lib:jsonable_map(emqx:get_config(Path)). + ChainName = emqx_gateway_utils:listener_chain(GwName, Type, Name), + wrap_chain_name( + ChainName, + emqx_map_lib:jsonable_map(emqx:get_config(Path)) + ). + +wrap_chain_name(ChainName, Conf) -> + case emqx_authentication:list_authenticators(ChainName) of + {ok, [#{id := Id} | _]} -> + Conf#{chain_name => ChainName, id => Id}; + _ -> + Conf + end. -spec add_authn(gateway_name(), map()) -> ok. add_authn(GwName, AuthConf) -> @@ -303,6 +321,13 @@ codestr(401) -> 'NOT_SUPPORTED_NOW'; codestr(404) -> 'RESOURCE_NOT_FOUND'; codestr(500) -> 'UNKNOW_ERROR'. +-spec with_authn(binary(), function()) -> any(). +with_authn(GwName0, Fun) -> + with_gateway(GwName0, fun(GwName) -> + Authn = emqx_gateway_http:authn(GwName), + Fun(GwName, Authn) + end). + -spec with_gateway(binary(), function()) -> any(). with_gateway(GwName0, Fun) -> try diff --git a/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl b/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl index 52c23d459..4c0417fa9 100644 --- a/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl +++ b/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl @@ -219,23 +219,6 @@ detailed_gateway_info(State) -> %% Internal funcs %%-------------------------------------------------------------------- -%% same with emqx_authentication:global_chain/1 -global_chain(mqtt) -> - 'mqtt:global'; -global_chain('mqtt-sn') -> - 'mqtt-sn:global'; -global_chain(coap) -> - 'coap:global'; -global_chain(lwm2m) -> - 'lwm2m:global'; -global_chain(stomp) -> - 'stomp:global'; -global_chain(_) -> - 'unknown:global'. - -listener_chain(GwName, Type, LisName) -> - emqx_gateway_utils:listener_id(GwName, Type, LisName). - %% There are two layer authentication configs %% stomp.authn %% / \ @@ -266,10 +249,11 @@ do_init_authn([_BadConf|More], Names) -> authns(GwName, Config) -> Listeners = maps:to_list(maps:get(listeners, Config, #{})), lists:append( - [ [{listener_chain(GwName, LisType, LisName), authn_conf(Opts)} + [ [{emqx_gateway_utils:listener_chain(GwName, LisType, LisName), + authn_conf(Opts)} || {LisName, Opts} <- maps:to_list(LisNames) ] || {LisType, LisNames} <- Listeners]) - ++ [{global_chain(GwName), authn_conf(Config)}]. + ++ [{emqx_gateway_utils:global_chain(GwName), authn_conf(Config)}]. authn_conf(Conf) -> maps:get(authentication, Conf, #{enable => false}). diff --git a/apps/emqx_gateway/src/emqx_gateway_utils.erl b/apps/emqx_gateway/src/emqx_gateway_utils.erl index a497e11d0..8c824a1a1 100644 --- a/apps/emqx_gateway/src/emqx_gateway_utils.erl +++ b/apps/emqx_gateway/src/emqx_gateway_utils.erl @@ -34,6 +34,8 @@ , listener_id/3 , parse_listener_id/1 , is_running/2 + , global_chain/1 + , listener_chain/3 ]). -export([ stringfy/1 @@ -159,6 +161,23 @@ is_running(ListenerId, #{<<"bind">> := ListenOn0}) -> false end. +%% same with emqx_authentication:global_chain/1 +global_chain(mqtt) -> + 'mqtt:global'; +global_chain('mqtt-sn') -> + 'mqtt-sn:global'; +global_chain(coap) -> + 'coap:global'; +global_chain(lwm2m) -> + 'lwm2m:global'; +global_chain(stomp) -> + 'stomp:global'; +global_chain(_) -> + 'unknown:global'. + +listener_chain(GwName, Type, LisName) -> + listener_id(GwName, Type, LisName). + bin(A) when is_atom(A) -> atom_to_binary(A); bin(L) when is_list(L); is_binary(L) -> diff --git a/apps/emqx_gateway/test/emqx_sn_frame_SUITE.erl b/apps/emqx_gateway/test/emqx_sn_frame_SUITE.erl index c02b60dd0..335e00b20 100644 --- a/apps/emqx_gateway/test/emqx_sn_frame_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_sn_frame_SUITE.erl @@ -164,7 +164,7 @@ t_random_test(_) -> random_test_body() -> Data = generate_random_binary(), case catch parse(Data) of - {ok, _Msg} -> ok; + Msg when is_record(Msg, mqtt_sn_message) -> ok; {'EXIT', {Err, _Stack}} when Err =:= unkown_message_type; Err =:= malformed_message_len; From 4f752fb5abb7b8b4d3baeb3aee765f9c121848ec Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 22 Nov 2021 10:14:13 +0800 Subject: [PATCH 2/5] chore(gw): fix elvis warnings --- apps/emqx_authn/src/emqx_authn_api.erl | 2 ++ .../src/emqx_gateway_api_authn.erl | 6 ++--- .../src/emqx_gateway_api_listeners.erl | 10 ++++---- apps/emqx_gateway/src/emqx_gateway_http.erl | 6 ++++- .../src/emqx_gateway_insta_sup.erl | 24 ++++++++++--------- apps/emqx_gateway/src/emqx_gateway_utils.erl | 8 ++++--- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index de3e0b5d3..74b1990fa 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -74,6 +74,8 @@ , serialize_error/1 ]). +-elvis([{elvis_style, god_modules, disable}]). + api_spec() -> emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). diff --git a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl index e529bf2ae..f5b6b3dff 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl @@ -173,7 +173,7 @@ schema("/gateway/:name/authentication") -> put => #{ description => <<"Update authentication for the gateway">> , parameters => params_gateway_name_in_path() - , requestBody => schema_authn() + , 'requestBody' => schema_authn() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -185,7 +185,7 @@ schema("/gateway/:name/authentication") -> post => #{ description => <<"Add authentication for the gateway">> , parameters => params_gateway_name_in_path() - , requestBody => schema_authn() + , 'requestBody' => schema_authn() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -289,7 +289,7 @@ schema("/gateway/:name/authentication/import_users") -> , post => #{ description => <<"Import users into the gateway authentication">> , parameters => params_gateway_name_in_path() - , requestBody => emqx_dashboard_swagger:schema_with_examples( + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(emqx_authn_api, request_import_users), emqx_authn_api:request_import_users_examples() ) diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index 25bfc5b5c..db3d3d96c 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -179,7 +179,7 @@ schema("/gateway/:name/listeners") -> post => #{ description => <<"Create the gateway listener">> , parameters => params_gateway_name_in_path() - , requestBody => emqx_dashboard_swagger:schema_with_examples( + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(listener), examples_listener()) , responses => @@ -223,7 +223,7 @@ schema("/gateway/:name/listeners/:id") -> #{ description => <<"Update the gateway listener">> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() - , requestBody => emqx_dashboard_swagger:schema_with_examples( + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(listener), examples_listener()) , responses => @@ -254,7 +254,7 @@ schema("/gateway/:name/listeners/:id/authentication") -> #{ description => <<"Add authentication for the listener">> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() - , requestBody => schema_authn() + , 'requestBody' => schema_authn() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -267,7 +267,7 @@ schema("/gateway/:name/listeners/:id/authentication") -> #{ description => <<"Update authentication for the listener">> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() - , requestBody => schema_authn() + , 'requestBody' => schema_authn() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -387,7 +387,7 @@ fields(dtls_listener_opts) -> lists_key_without([], _N, L) -> L; -lists_key_without([K|Ks], N, L) -> +lists_key_without([K | Ks], N, L) -> lists_key_without(Ks, N, lists:keydelete(K, N, L)). common_listener_opts() -> diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index 41923a66e..52fba3c70 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -70,6 +70,10 @@ , listeners => [] }. +-elvis([{elvis_style, god_modules, disable}]). +-elvis([{elvis_style, no_nested_try_catch, disable}]). + + -define(DEFAULT_CALL_TIMEOUT, 15000). %%-------------------------------------------------------------------- @@ -371,7 +375,7 @@ with_gateway(GwName0, Fun) -> -spec checks(list(), map()) -> ok. checks([], _) -> ok; -checks([K|Ks], Map) -> +checks([K | Ks], Map) -> case maps:is_key(K, Map) of true -> checks(Ks, Map); false -> diff --git a/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl b/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl index 4c0417fa9..efb3f6fe6 100644 --- a/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl +++ b/apps/emqx_gateway/src/emqx_gateway_insta_sup.erl @@ -52,6 +52,8 @@ stopped_at :: integer() | undefined }). +-elvis([{elvis_style, invalid_dynamic_call, disable}]). + %%-------------------------------------------------------------------- %% APIs %%-------------------------------------------------------------------- @@ -237,13 +239,13 @@ init_authn(GwName, Config) -> do_init_authn([], Names) -> Names; -do_init_authn([{_ChainName, _AuthConf = #{enable := false}}|More], Names) -> +do_init_authn([{_ChainName, _AuthConf = #{enable := false}} | More], Names) -> do_init_authn(More, Names); -do_init_authn([{ChainName, AuthConf}|More], Names) when is_map(AuthConf) -> +do_init_authn([{ChainName, AuthConf} | More], Names) when is_map(AuthConf) -> _ = application:ensure_all_started(emqx_authn), do_create_authn_chain(ChainName, AuthConf), - do_init_authn(More, [ChainName|Names]); -do_init_authn([_BadConf|More], Names) -> + do_init_authn(More, [ChainName | Names]); +do_init_authn([_BadConf | More], Names) -> do_init_authn(More, Names). authns(GwName, Config) -> @@ -312,13 +314,13 @@ do_update_one_by_one(NCfg, State = #state{ OAuths = authns(GwName, OCfg), NAuths = authns(GwName, NCfg), - if - Status == stopped, NEnable == true -> + case {Status, NEnable} of + {stopped, true} -> NState = State#state{config = NCfg}, cb_gateway_load(NState); - Status == stopped, NEnable == false -> + {stopped, false} -> {ok, State#state{config = NCfg}}; - Status == running, NEnable == true -> + {running, true} -> NState = case NAuths == OAuths of true -> State; false -> @@ -329,12 +331,12 @@ do_update_one_by_one(NCfg, State = #state{ end, %% XXX: minimum impact update ??? cb_gateway_update(NCfg, NState); - Status == running, NEnable == false -> + {running, false} -> case cb_gateway_unload(State) of {ok, NState} -> {ok, NState#state{config = NCfg}}; {error, Reason} -> {error, Reason} end; - true -> + _ -> throw(nomatch) end. @@ -432,7 +434,7 @@ cb_gateway_update(Config, end. start_child_process([]) -> []; -start_child_process([Indictor|_] = ChildPidOrSpecs) -> +start_child_process([Indictor | _] = ChildPidOrSpecs) -> case erlang:is_pid(Indictor) of true -> ChildPidOrSpecs; diff --git a/apps/emqx_gateway/src/emqx_gateway_utils.erl b/apps/emqx_gateway/src/emqx_gateway_utils.erl index 8c824a1a1..5d17fe6ca 100644 --- a/apps/emqx_gateway/src/emqx_gateway_utils.erl +++ b/apps/emqx_gateway/src/emqx_gateway_utils.erl @@ -66,6 +66,8 @@ -define(DEFAULT_OOM_POLICY, #{max_heap_size => 4194304, message_queue_len => 32000}). +-elvis([{elvis_style, god_modules, disable}]). + -spec childspec(supervisor:worker(), Mod :: atom()) -> supervisor:child_spec(). childspec(Type, Mod) -> @@ -202,7 +204,7 @@ stringfy(T) when is_list(T); is_binary(T) -> stringfy(T) -> iolist_to_binary(io_lib:format("~0p", [T])). --spec parse_address(binary()|list()) -> {list(), integer()}. +-spec parse_address(binary() | list()) -> {list(), integer()}. parse_address(S) when is_binary(S); is_list(S) -> S1 = case is_binary(S) of true -> lists:reverse(binary_to_list(S)); @@ -234,9 +236,9 @@ normalize_config(RawConf) -> [bind, tcp, ssl, udp, dtls] ++ proplists:get_keys(SocketOpts), Confs), Cfg = maps:merge(Cfg0, RemainCfgs), - [{Type, Name, ListenOn, SocketOpts, Cfg}|AccIn2] + [{Type, Name, ListenOn, SocketOpts, Cfg} | AccIn2] end, [], Liss), - [Listeners|AccIn1] + [Listeners | AccIn1] end, [], LisMap)). esockd_opts(Type, Opts0) -> From 7d9930b64e2b3bdbf4cd47fe77577b4d61a63667 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 22 Nov 2021 11:47:03 +0800 Subject: [PATCH 3/5] feat(gw): support data-management apis gw's authn --- .../src/emqx_gateway_api_listeners.erl | 180 ++++++++++++++++++ apps/emqx_gateway/src/emqx_gateway_http.erl | 8 + 2 files changed, 188 insertions(+) diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index db3d3d96c..62b871fa0 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -30,6 +30,7 @@ -import(emqx_gateway_http, [ return_http_error/2 , with_gateway/2 + , with_listener_authn/3 , checks/2 ]). @@ -49,6 +50,9 @@ -export([ listeners/2 , listeners_insta/2 , listeners_insta_authn/2 + , users/2 + , users_insta/2 + , import_users/2 ]). %%-------------------------------------------------------------------- @@ -62,6 +66,9 @@ paths() -> [ "/gateway/:name/listeners" , "/gateway/:name/listeners/:id" , "/gateway/:name/listeners/:id/authentication" + , "/gateway/:name/listeners/:id/authentication/users" + , "/gateway/:name/listeners/:id/authentication/users/:uid" + , "/gateway/:name/listeners/:id/authentication/import_users" ]. %%-------------------------------------------------------------------- @@ -157,6 +164,58 @@ listeners_insta_authn(delete, #{bindings := #{name := Name0, {204} end). +users(get, #{bindings := #{name := Name0, id := Id}, query_string := Qs}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + emqx_authn_api:list_users(ChainName, AuthId, page_pramas(Qs)) + end); +users(post, #{bindings := #{name := Name0, id := Id}, + body := Body}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + emqx_authn_api:add_user(ChainName, AuthId, Body) + end). + +users_insta(get, #{bindings := #{name := Name0, id := Id, uid := UserId}}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + emqx_authn_api:find_user(ChainName, AuthId, UserId) + end); +users_insta(put, #{bindings := #{name := Name0, id := Id, uid := UserId}, + body := Body}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + emqx_authn_api:update_user(ChainName, AuthId, UserId, Body) + end); +users_insta(delete, #{bindings := #{name := Name0, id := Id, uid := UserId}}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + emqx_authn_api:delete_user(ChainName, AuthId, UserId) + end). + +import_users(post, #{bindings := #{name := Name0, id := Id}, + body := Body}) -> + with_listener_authn(Name0, Id, + fun(_GwName, #{id := AuthId, chain_name := ChainName}) -> + case maps:get(<<"filename">>, Body, undefined) of + undefined -> + emqx_authn_api:serialize_error({missing_parameter, filename}); + Filename -> + case emqx_authentication:import_users( + ChainName, AuthId, Filename) of + ok -> {204}; + {error, Reason} -> + emqx_authn_api:serialize_error(Reason) + end + end + end). + +%%-------------------------------------------------------------------- +%% Utils + +page_pramas(Qs) -> + maps:with([<<"page">>, <<"limit">>], Qs). + %%-------------------------------------------------------------------- %% Swagger defines %%-------------------------------------------------------------------- @@ -288,6 +347,109 @@ schema("/gateway/:name/listeners/:id/authentication") -> , 204 => <<"Deleted">> } } + }; +schema("/gateway/:name/listeners/:id/authentication/users") -> + #{ 'operationId' => users + , get => + #{ description => <<"Get the users for the authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + post => + #{ description => <<"Add user for the authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 201 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + } + }; +schema("/gateway/:name/listeners/:id/authentication/users/:uid") -> + #{ 'operationId' => users_insta + , get => + #{ description => <<"Get user info from the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() ++ + params_userid_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + put => + #{ description => <<"Update the user info for the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() ++ + params_userid_in_path() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + }, + delete => + #{ description => <<"Delete the user for the gateway " + "authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() ++ + params_userid_in_path() ++ + params_paging_in_qs() + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + , 200 => emqx_dashboard_swagger:schema_with_example( + ref(emqx_authn_api, response_user), + emqx_authn_api:response_user_examples()) + } + } + }; +schema("/gateway/:name/listeners/:id/authentication/import_users") -> + #{ 'operationId' => import_users + , post => + #{ description => <<"Import users into the gateway authentication">> + , parameters => params_gateway_name_in_path() ++ + params_listener_id_in_path() + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_import_users), + emqx_authn_api:request_import_users_examples() + ) + , responses => + #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + , 500 => error_codes([?INTERNAL_ERROR], + <<"Ineternal Server Error">>) + %% XXX: Put a hint message into 204 return ? + , 204 => <<"Imported">> + } + } }. %%-------------------------------------------------------------------- @@ -309,6 +471,24 @@ params_listener_id_in_path() -> })} ]. +params_userid_in_path() -> + [{uid, mk(binary(), + #{ in => path + , desc => <<"User ID">> + })} + ]. + +params_paging_in_qs() -> + [{page, mk(integer(), + #{ in => query + , desc => <<"Page Number">> + })}, + {limit, mk(integer(), + #{ in => query + , desc => <<"Page Limit">> + })} + ]. + %%-------------------------------------------------------------------- %% schemas diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index 52fba3c70..a35a4d504 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -54,6 +54,7 @@ -export([ return_http_error/2 , with_gateway/2 , with_authn/2 + , with_listener_authn/3 , checks/2 , schema_bad_request/0 , schema_not_found/0 @@ -332,6 +333,13 @@ with_authn(GwName0, Fun) -> Fun(GwName, Authn) end). +-spec with_listener_authn(binary(), binary(), function()) -> any(). +with_listener_authn(GwName0, Id, Fun) -> + with_gateway(GwName0, fun(GwName) -> + Authn = emqx_gateway_http:authn(GwName, Id), + Fun(GwName, Authn) + end). + -spec with_gateway(binary(), function()) -> any(). with_gateway(GwName0, Fun) -> try From 4eb27ce25bf4ec6622552a87ee2babd15f6e069b Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 22 Nov 2021 14:37:44 +0800 Subject: [PATCH 4/5] fix(gw): fix bad paging params --- .../src/emqx_gateway_api_authn.erl | 18 +++++++++++++---- .../src/emqx_gateway_api_listeners.erl | 20 +++++++++++++------ apps/emqx_gateway/src/emqx_gateway_http.erl | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl index f5b6b3dff..cd6c46b29 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl @@ -82,6 +82,7 @@ authn(get, #{bindings := #{name := Name0}}) -> authn(put, #{bindings := #{name := Name0}, body := Body}) -> with_gateway(Name0, fun(GwName, _) -> + %% TODO: return the authn instances? ok = emqx_gateway_http:update_authn(GwName, Body), {204} end); @@ -89,6 +90,7 @@ authn(put, #{bindings := #{name := Name0}, authn(post, #{bindings := #{name := Name0}, body := Body}) -> with_gateway(Name0, fun(GwName, _) -> + %% TODO: return the authn instances? ok = emqx_gateway_http:add_authn(GwName, Body), {204} end); @@ -210,7 +212,8 @@ schema("/gateway/:name/authentication/users") -> #{ 'operationId' => users , get => #{ description => <<"Get the users for the authentication">> - , parameters => params_gateway_name_in_path() + , parameters => params_gateway_name_in_path() ++ + params_paging_in_qs() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -224,6 +227,9 @@ schema("/gateway/:name/authentication/users") -> post => #{ description => <<"Add user for the authentication">> , parameters => params_gateway_name_in_path() + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_user_create), + emqx_authn_api:request_user_create_examples()) , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -257,6 +263,9 @@ schema("/gateway/:name/authentication/users/:uid") -> "authentication">> , parameters => params_gateway_name_in_path() ++ params_userid_in_path() + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_user_update), + emqx_authn_api:request_user_update_examples()) , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -271,8 +280,7 @@ schema("/gateway/:name/authentication/users/:uid") -> #{ description => <<"Delete the user for the gateway " "authentication">> , parameters => params_gateway_name_in_path() ++ - params_userid_in_path() ++ - params_paging_in_qs() + params_userid_in_path() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -325,10 +333,12 @@ params_userid_in_path() -> params_paging_in_qs() -> [{page, mk(integer(), #{ in => query - , desc => <<"Page Number">> + , nullable => true + , desc => <<"Page Index">> })}, {limit, mk(integer(), #{ in => query + , nullable => true , desc => <<"Page Limit">> })} ]. diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index 62b871fa0..f1744363c 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -353,7 +353,8 @@ schema("/gateway/:name/listeners/:id/authentication/users") -> , get => #{ description => <<"Get the users for the authentication">> , parameters => params_gateway_name_in_path() ++ - params_listener_id_in_path() + params_listener_id_in_path() ++ + params_paging_in_qs() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -368,6 +369,9 @@ schema("/gateway/:name/listeners/:id/authentication/users") -> #{ description => <<"Add user for the authentication">> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_user_create), + emqx_authn_api:request_user_create_examples()) , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -403,6 +407,9 @@ schema("/gateway/:name/listeners/:id/authentication/users/:uid") -> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() ++ params_userid_in_path() + , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(emqx_authn_api, request_user_update), + emqx_authn_api:request_user_update_examples()) , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -418,8 +425,7 @@ schema("/gateway/:name/listeners/:id/authentication/users/:uid") -> "authentication">> , parameters => params_gateway_name_in_path() ++ params_listener_id_in_path() ++ - params_userid_in_path() ++ - params_paging_in_qs() + params_userid_in_path() , responses => #{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) @@ -481,10 +487,12 @@ params_userid_in_path() -> params_paging_in_qs() -> [{page, mk(integer(), #{ in => query - , desc => <<"Page Number">> + , nullable => true + , desc => <<"Page Index">> })}, {limit, mk(integer(), #{ in => query + , nullable => true , desc => <<"Page Limit">> })} ]. @@ -630,7 +638,7 @@ common_listener_opts() -> %% examples examples_listener_list() -> - [examples_listener()]. + #{stomp_listeners => [examples_listener()]}. examples_listener() -> - #{id => true}. + #{}. diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index a35a4d504..345335ff3 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -328,7 +328,7 @@ codestr(500) -> 'UNKNOW_ERROR'. -spec with_authn(binary(), function()) -> any(). with_authn(GwName0, Fun) -> - with_gateway(GwName0, fun(GwName) -> + with_gateway(GwName0, fun(GwName, _GwConf) -> Authn = emqx_gateway_http:authn(GwName), Fun(GwName, Authn) end). From 1e2eac0fce9ba80c6d2e13196daad689af9ad296 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 22 Nov 2021 17:09:08 +0800 Subject: [PATCH 5/5] test(gw): add tests for authm data-mgmt --- apps/emqx_authn/src/emqx_authn_api.erl | 7 +- .../src/emqx_gateway_api_authn.erl | 4 +- apps/emqx_gateway/src/emqx_gateway_http.erl | 2 +- .../test/emqx_gateway_api_SUITE.erl | 103 ++++++++++++++++++ 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 74b1990fa..dcd65dd21 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -796,11 +796,12 @@ add_user(_, _, #{<<"user_id">> := _}) -> add_user(_, _, _) -> serialize_error({missing_parameter, user_id}). -update_user(ChainName, AuthenticatorID, UserID, UserInfo) -> - case maps:with([<<"password">>, <<"is_superuser">>], UserInfo) =:= #{} of +update_user(ChainName, AuthenticatorID, UserID, UserInfo0) -> + case maps:with([<<"password">>, <<"is_superuser">>], UserInfo0) =:= #{} of true -> serialize_error({missing_parameter, password}); false -> + UserInfo = emqx_map_lib:safe_atom_key_map(UserInfo0), case emqx_authentication:update_user(ChainName, AuthenticatorID, UserID, UserInfo) of {ok, User} -> {200, User}; @@ -907,7 +908,7 @@ serialize_error({bad_ssl_config, Details}) -> message => binfmt("bad_ssl_config ~p", [Details])}}; serialize_error({missing_parameter, Detail}) -> {400, #{code => <<"MISSING_PARAMETER">>, - message => binfmt("Missing required parameter", [Detail])}}; + message => binfmt("Missing required parameter: ~p", [Detail])}}; serialize_error({invalid_parameter, Name}) -> {400, #{code => <<"INVALID_PARAMETER">>, message => binfmt("Invalid value for '~p'", [Name])}}; diff --git a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl index cd6c46b29..105a96989 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl @@ -286,9 +286,7 @@ schema("/gateway/:name/authentication/users/:uid") -> , 404 => error_codes([?NOT_FOUND], <<"Not Found">>) , 500 => error_codes([?INTERNAL_ERROR], <<"Ineternal Server Error">>) - , 200 => emqx_dashboard_swagger:schema_with_example( - ref(emqx_authn_api, response_user), - emqx_authn_api:response_user_examples()) + , 204 => <<"User Deleted">> } } }; diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index 345335ff3..0d2f765c5 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -335,7 +335,7 @@ with_authn(GwName0, Fun) -> -spec with_listener_authn(binary(), binary(), function()) -> any(). with_listener_authn(GwName0, Id, Fun) -> - with_gateway(GwName0, fun(GwName) -> + with_gateway(GwName0, fun(GwName, _GwConf) -> Authn = emqx_gateway_http:authn(GwName, Id), Fun(GwName, Authn) end). diff --git a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl index 0998cab31..e49a78e73 100644 --- a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl @@ -207,6 +207,50 @@ t_authn(_) -> {204, _} = request(get, "/gateway/stomp/authentication"), {204, _} = request(delete, "/gateway/stomp"). +t_authn_data_mgmt(_) -> + GwConf = #{name => <<"stomp">>}, + {204, _} = request(post, "/gateway", GwConf), + {204, _} = request(get, "/gateway/stomp/authentication"), + + AuthConf = #{mechanism => <<"password-based">>, + backend => <<"built-in-database">>, + user_id_type => <<"clientid">> + }, + {204, _} = request(post, "/gateway/stomp/authentication", AuthConf), + {200, ConfResp} = request(get, "/gateway/stomp/authentication"), + assert_confs(AuthConf, ConfResp), + + User1 = #{ user_id => <<"test">> + , password => <<"123456">> + , is_superuser => false + }, + {201, _} = request(post, "/gateway/stomp/authentication/users", User1), + {200, #{data := [UserRespd1]}} = request(get, "/gateway/stomp/authentication/users"), + assert_confs(UserRespd1, User1), + + {200, UserRespd2} = request(get, + "/gateway/stomp/authentication/users/test"), + assert_confs(UserRespd2, User1), + + {200, UserRespd3} = request(put, + "/gateway/stomp/authentication/users/test", + #{password => <<"654321">>, + is_superuser => true}), + assert_confs(UserRespd3, User1#{is_superuser => true}), + + {200, UserRespd4} = request(get, + "/gateway/stomp/authentication/users/test"), + assert_confs(UserRespd4, User1#{is_superuser => true}), + + {204, _} = request(delete, "/gateway/stomp/authentication/users/test"), + + {200, #{data := []}} = request(get, + "/gateway/stomp/authentication/users"), + + {204, _} = request(delete, "/gateway/stomp/authentication"), + {204, _} = request(get, "/gateway/stomp/authentication"), + {204, _} = request(delete, "/gateway/stomp"). + t_listeners(_) -> GwConf = #{name => <<"stomp">>}, {204, _} = request(post, "/gateway", GwConf), @@ -262,6 +306,65 @@ t_listeners_authn(_) -> assert_confs(AuthConf2, ConfResp3), {204, _} = request(delete, "/gateway/stomp"). +t_listeners_authn_data_mgmt(_) -> + GwConf = #{name => <<"stomp">>, + listeners => [ + #{name => <<"def">>, + type => <<"tcp">>, + bind => <<"61613">> + }]}, + {204, _} = request(post, "/gateway", GwConf), + {200, ConfResp} = request(get, "/gateway/stomp"), + assert_confs(GwConf, ConfResp), + + AuthConf = #{mechanism => <<"password-based">>, + backend => <<"built-in-database">>, + user_id_type => <<"clientid">> + }, + Path = "/gateway/stomp/listeners/stomp:tcp:def/authentication", + {204, _} = request(post, Path, AuthConf), + {200, ConfResp2} = request(get, Path), + assert_confs(AuthConf, ConfResp2), + + User1 = #{ user_id => <<"test">> + , password => <<"123456">> + , is_superuser => false + }, + {201, _} = request(post, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users", + User1), + + {200, + #{data := [UserRespd1]} } = request( + get, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users"), + assert_confs(UserRespd1, User1), + + {200, UserRespd2} = request( + get, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"), + assert_confs(UserRespd2, User1), + + {200, UserRespd3} = request( + put, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test", + #{password => <<"654321">>, is_superuser => true}), + assert_confs(UserRespd3, User1#{is_superuser => true}), + + {200, UserRespd4} = request( + get, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"), + assert_confs(UserRespd4, User1#{is_superuser => true}), + + {204, _} = request( + delete, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"), + + {200, #{data := []}} = request( + get, + "/gateway/stomp/listeners/stomp:tcp:def/authentication/users"), + {204, _} = request(delete, "/gateway/stomp"). + %%-------------------------------------------------------------------- %% Asserts