From 4cae80c8d2536a28862db0c1d84f37baabbf9b3f Mon Sep 17 00:00:00 2001 From: Ilya Averyanov Date: Mon, 30 May 2022 15:31:51 +0300 Subject: [PATCH] fix(authn api): add method for removing listener-specific chains --- apps/emqx/src/emqx_authentication_config.erl | 22 ++++++ apps/emqx_authn/i18n/emqx_authn_api_i18n.conf | 7 ++ apps/emqx_authn/src/emqx_authn_api.erl | 26 +++++++ apps/emqx_authn/test/emqx_authn_api_SUITE.erl | 78 +++++++++++++++++++ 4 files changed, 133 insertions(+) diff --git a/apps/emqx/src/emqx_authentication_config.erl b/apps/emqx/src/emqx_authentication_config.erl index 93fcafc35..c826a0fac 100644 --- a/apps/emqx/src/emqx_authentication_config.erl +++ b/apps/emqx/src/emqx_authentication_config.erl @@ -52,6 +52,7 @@ -type update_request() :: {create_authenticator, chain_name(), map()} | {delete_authenticator, chain_name(), authenticator_id()} + | {delete_authenticators, chain_name()} | {update_authenticator, chain_name(), authenticator_id(), map()} | {move_authenticator, chain_name(), authenticator_id(), position()}. @@ -88,6 +89,8 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon OldConfig ), {ok, NewConfig}; +do_pre_config_update({delete_authenticators, _ChainName}, _OldConfig) -> + {ok, []}; do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) -> CertsDir = certs_dir(ChainName, AuthenticatorID), NewConfig = lists:map( @@ -156,6 +159,25 @@ do_post_config_update( {error, Reason} -> {error, Reason} end; +do_post_config_update( + {delete_authenticators, ChainName}, + _NewConfig, + OldConfig, + _AppEnvs +) -> + case emqx_authentication:delete_chain(ChainName) of + ok -> + lists:foreach( + fun(Config) -> + AuthenticatorID = authenticator_id(Config), + CertsDir = certs_dir(ChainName, AuthenticatorID), + ok = clear_certs(CertsDir, Config) + end, + to_list(OldConfig) + ); + {error, Reason} -> + {error, Reason} + end; do_post_config_update( {update_authenticator, ChainName, AuthenticatorID, Config}, NewConfig, diff --git a/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf index 3b8093e63..44725e935 100644 --- a/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf +++ b/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf @@ -49,6 +49,13 @@ emqx_authn_api { } } + listeners_listener_id_authentication_delete { + desc { + en: """Delete listener-specific authentication.""" + zh: """删除特定于侦听器的身份验证。""" + } + } + listeners_listener_id_authentication_post { desc { en: """Create authenticator for listener authentication.""" diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 1f08cf1f2..4effc24ff 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -253,6 +253,15 @@ schema("/listeners/:listener_id/authentication") -> ) } }, + delete => #{ + tags => ?API_TAGS_SINGLE, + description => ?DESC(listeners_listener_id_authentication_delete), + parameters => [param_listener_id()], + responses => #{ + 204 => <<"Authentication chain deleted">>, + 404 => error_codes([?NOT_FOUND], <<"Not Found">>) + } + }, post => #{ tags => ?API_TAGS_SINGLE, description => ?DESC(listeners_listener_id_authentication_post), @@ -642,6 +651,13 @@ listener_authenticators(get, #{bindings := #{listener_id := ListenerID}}) -> fun(Type, Name, _) -> list_authenticators([listeners, Type, Name, authentication]) end + ); +listener_authenticators(delete, #{bindings := #{listener_id := ListenerID}}) -> + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + delete_authenticators([listeners, Type, Name, authentication], ChainName) + end ). listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) -> @@ -1094,6 +1110,16 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) -> serialize_error(Reason) end. +delete_authenticators(ConfKeyPath, ChainName) -> + case update_config(ConfKeyPath, {delete_authenticators, ChainName}) of + {ok, _} -> + {204}; + {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> + serialize_error(Reason); + {error, Reason} -> + serialize_error(Reason) + end. + move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) -> case parse_position(Position) of {ok, NPosition} -> diff --git a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl index 86fc1f66c..c61f52119 100644 --- a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl @@ -657,6 +657,84 @@ test_authenticator_import_users(PathPrefix) -> {ok, 204, _} = request(post, ImportUri, #{filename => CSVFileName}). +t_switch_to_global_chain(_) -> + {ok, 200, _} = request( + post, + uri([?CONF_NS]), + emqx_authn_test_lib:built_in_database_example() + ), + + {ok, 200, _} = request( + post, + uri([listeners, "tcp:default", ?CONF_NS]), + emqx_authn_test_lib:built_in_database_example() + ), + + GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>}, + + {ok, 201, _} = request( + post, + uri([?CONF_NS, "password_based:built_in_database", "users"]), + GlobalUser + ), + + ListenerUser = #{user_id => <<"listener_user">>, password => <<"p1">>}, + + {ok, 201, _} = request( + post, + uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database", "users"]), + ListenerUser + ), + + process_flag(trap_exit, true), + + %% Listener user should be OK + {ok, Client0} = emqtt:start_link([ + {username, <<"listener_user">>}, + {password, <<"p1">>} + ]), + ?assertMatch( + {ok, _}, + emqtt:connect(Client0) + ), + ok = emqtt:disconnect(Client0), + + %% Global user should not be OK + {ok, Client1} = emqtt:start_link([ + {username, <<"global_user">>}, + {password, <<"p1">>} + ]), + ?assertMatch( + {error, {unauthorized_client, _}}, + emqtt:connect(Client1) + ), + + {ok, 204, _} = request( + delete, + uri([listeners, "tcp:default", ?CONF_NS]) + ), + + %% Listener user should not be OK — local chain removed + {ok, Client2} = emqtt:start_link([ + {username, <<"listener_user">>}, + {password, <<"p1">>} + ]), + ?assertMatch( + {error, {unauthorized_client, _}}, + emqtt:connect(Client2) + ), + + %% Global user should be now OK, switched back to the global chain + {ok, Client3} = emqtt:start_link([ + {username, <<"global_user">>}, + {password, <<"p1">>} + ]), + ?assertMatch( + {ok, _}, + emqtt:connect(Client3) + ), + ok = emqtt:disconnect(Client3). + %%------------------------------------------------------------------------------ %% Helpers %%------------------------------------------------------------------------------