diff --git a/apps/emqx/include/emqx_access_control.hrl b/apps/emqx/include/emqx_access_control.hrl index 08146339b..f7348899b 100644 --- a/apps/emqx/include/emqx_access_control.hrl +++ b/apps/emqx/include/emqx_access_control.hrl @@ -14,6 +14,9 @@ %% limitations under the License. %%-------------------------------------------------------------------- +-ifndef(EMQX_ACCESS_CONTROL_HRL). +-define(EMQX_ACCESS_CONTROL_HRL, true). + %% config root name all auth providers have to agree on. -define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME, "authorization"). -define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM, authorization). @@ -35,3 +38,4 @@ -define(AUTHN_TRACE_TAG, "AUTHN"). +-endif. diff --git a/apps/emqx/include/emqx_hooks.hrl b/apps/emqx/include/emqx_hooks.hrl index 2373b5928..dffc09fcd 100644 --- a/apps/emqx/include/emqx_hooks.hrl +++ b/apps/emqx/include/emqx_hooks.hrl @@ -29,6 +29,7 @@ -define(HP_RETAINER, 930). -define(HP_AUTO_SUB, 920). -define(HP_RULE_ENGINE, 900). + %% apps that can work with the republish action -define(HP_SLOW_SUB, 880). -define(HP_BRIDGE, 870). diff --git a/apps/emqx/src/emqx_authentication_listener_hooks.erl b/apps/emqx/src/emqx_authentication_listener_hooks.erl new file mode 100644 index 000000000..1109ca96b --- /dev/null +++ b/apps/emqx/src/emqx_authentication_listener_hooks.erl @@ -0,0 +1,86 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2017-2023 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_authentication_listener_hooks). + +-include_lib("emqx/include/emqx_hooks.hrl"). + +-export([ + on_listener_started/4, + on_listener_stopped/4, + on_listener_updated/4 +]). + +-export([ + load/0, + unload/0 +]). + +%%-------------------------------------------------------------------- +%% API +%%-------------------------------------------------------------------- + +load() -> + ok = emqx_hook:put('listener.started', {?MODULE, on_listener_started, []}, ?HP_AUTHN), + ok = emqx_hook:put('listener.stopped', {?MODULE, on_listener_stopped, []}, ?HP_AUTHN), + ok = emqx_hook:put('listener.updated', {?MODULE, on_listener_updated, []}, ?HP_AUTHN), + ok. + +unload() -> + ok = emqx_hooks:del('listener.started', {?MODULE, authenticate, []}), + ok = emqx_hooks:del('listener.stopped', {?MODULE, authenticate, []}), + ok = emqx_hooks:del('listener.updated', {?MODULE, authenticate, []}), + ok. + +%%-------------------------------------------------------------------- +%% Hooks +%%-------------------------------------------------------------------- + +on_listener_started(Type, Name, Conf, ok) -> + recreate_authenticators(Type, Name, Conf); +on_listener_started(_Type, _Name, _Conf, _Error) -> + ok. + +on_listener_updated(Type, Name, {_OldConf, NewConf}, ok) -> + recreate_authenticators(Type, Name, NewConf); +on_listener_updated(_Type, _Name, _Conf, _Error) -> + ok. + +on_listener_stopped(Type, Name, _OldConf, ok) -> + _ = emqx_authentication:delete_chain(emqx_listeners:listener_id(Type, Name)), + ok; +on_listener_stopped(_Type, _Name, _Conf, _Error) -> + ok. + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +recreate_authenticators(Type, Name, Conf) -> + Chain = emqx_listeners:listener_id(Type, Name), + _ = emqx_authentication:delete_chain(Chain), + do_create_authneticators(Chain, maps:get(authentication, Conf, [])). + +do_create_authneticators(Chain, [AuthN | T]) -> + case emqx_authentication:create_authenticator(Chain, AuthN) of + {ok, _} -> + do_create_authneticators(Chain, T); + Error -> + _ = emqx_authentication:delete_chain(Chain), + {ok, Error} + end; +do_create_authneticators(_Chain, []) -> + ok. diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index 964873e53..5d641de39 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -531,41 +531,18 @@ post_config_update(_Path, _Request, _NewConf, _OldConf, _AppEnvs) -> ok. create_listener(Type, Name, NewConf) -> - Res = start_listener(Type, Name, NewConf), - recreate_authenticators(Res, Type, Name, NewConf). - -recreate_authenticators(ok, Type, Name, Conf) -> - Chain = listener_id(Type, Name), - _ = emqx_authentication:delete_chain(Chain), - do_create_authneticators(Chain, maps:get(authentication, Conf, [])); -recreate_authenticators(Error, _Type, _Name, _NewConf) -> - Error. - -do_create_authneticators(Chain, [AuthN | T]) -> - case emqx_authentication:create_authenticator(Chain, AuthN) of - {ok, _} -> - do_create_authneticators(Chain, T); - Error -> - _ = emqx_authentication:delete_chain(Chain), - Error - end; -do_create_authneticators(_Chain, []) -> - ok. + StartRes = start_listener(Type, Name, NewConf), + emqx_hooks:run_fold('listener.started', [Type, Name, NewConf], StartRes). remove_listener(Type, Name, OldConf) -> ok = unregister_ocsp_stapling_refresh(Type, Name), - case stop_listener(Type, Name, OldConf) of - ok -> - _ = emqx_authentication:delete_chain(listener_id(Type, Name)), - ok; - Err -> - Err - end. + StopRes = stop_listener(Type, Name, OldConf), + emqx_hooks:run_fold('listener.stopped', [Type, Name, OldConf], StopRes). update_listener(Type, Name, {OldConf, NewConf}) -> ok = maybe_unregister_ocsp_stapling_refresh(Type, Name, NewConf), - Res = restart_listener(Type, Name, {OldConf, NewConf}), - recreate_authenticators(Res, Type, Name, NewConf). + RestartRes = restart_listener(Type, Name, {OldConf, NewConf}), + emqx_hooks:run_fold('listener.restarted', [Type, Name, {OldConf, NewConf}], RestartRes). perform_listener_changes([]) -> ok; diff --git a/apps/emqx/test/emqx_broker_SUITE.erl b/apps/emqx/test/emqx_broker_SUITE.erl index 6e03971a5..ba5438641 100644 --- a/apps/emqx/test/emqx_broker_SUITE.erl +++ b/apps/emqx/test/emqx_broker_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_hooks.hrl"). -include_lib("emqx/include/emqx_mqtt.hrl"). all() -> @@ -680,28 +681,17 @@ t_connect_client_never_negative({'end', _Config}) -> t_connack_auth_error({init, Config}) -> process_flag(trap_exit, true), - ChainName = 'mqtt:global', - AuthenticatorConfig = #{ - enable => true, - mechanism => password_based, - backend => built_in_database, - user_id_type => username, - password_hash_algorithm => #{ - name => plain, - salt_position => disable - }, - user_group => <<"global:mqtt">> - }, - ok = emqx_authentication:register_providers( - [{{password_based, built_in_database}, emqx_authentication_SUITE}] + emqx_hooks:put( + 'client.authenticate', + {?MODULE, authenticate_deny, []}, + ?HP_AUTHN ), - emqx_authentication:initialize_authentication(ChainName, AuthenticatorConfig), Config; t_connack_auth_error({'end', _Config}) -> - ChainName = 'mqtt:global', - AuthenticatorID = <<"password_based:built_in_database">>, - ok = emqx_authentication:deregister_provider({password_based, built_in_database}), - ok = emqx_authentication:delete_authenticator(ChainName, AuthenticatorID), + emqx_hooks:del( + 'client.authenticate', + {?MODULE, authenticate_deny, []} + ), ok; t_connack_auth_error(Config) when is_list(Config) -> %% MQTT 3.1 @@ -733,6 +723,9 @@ t_handle_in_empty_client_subscribe_hook(Config) when is_list(Config) -> emqtt:disconnect(C) end. +authenticate_deny(_Credentials, _Default) -> + {stop, {error, bad_username_or_password}}. + wait_for_events(Action, Kinds) -> wait_for_events(Action, Kinds, 500).