Merge pull request #14359 from rabbitmq/verify-auth-plugin-is-enabled
rabbit_access_control: Check configured auth backends are enabled at boot time
This commit is contained in:
		
						commit
						cf3fd1e7c7
					
				| 
						 | 
					@ -52,6 +52,14 @@
 | 
				
			||||||
                    {requires,    pre_boot},
 | 
					                    {requires,    pre_boot},
 | 
				
			||||||
                    {enables,     external_infrastructure}]}).
 | 
					                    {enables,     external_infrastructure}]}).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-rabbit_boot_step({auth_backend_plugins_check,
 | 
				
			||||||
 | 
					                   [{description, "check configured auth plugins are enabled"},
 | 
				
			||||||
 | 
					                    {mfa,         {rabbit_access_control,
 | 
				
			||||||
 | 
					                                   ensure_auth_backends_are_enabled,
 | 
				
			||||||
 | 
					                                   []}},
 | 
				
			||||||
 | 
					                    {requires,    pre_boot},
 | 
				
			||||||
 | 
					                    {enables,     external_infrastructure}]}).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%% rabbit_alarm currently starts memory and disk space monitors
 | 
					%% rabbit_alarm currently starts memory and disk space monitors
 | 
				
			||||||
-rabbit_boot_step({rabbit_alarm,
 | 
					-rabbit_boot_step({rabbit_alarm,
 | 
				
			||||||
                   [{description, "alarm handler"},
 | 
					                   [{description, "alarm handler"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
-include_lib("rabbit_common/include/rabbit.hrl").
 | 
					-include_lib("rabbit_common/include/rabbit.hrl").
 | 
				
			||||||
-include_lib("kernel/include/logger.hrl").
 | 
					-include_lib("kernel/include/logger.hrl").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-export([ensure_auth_backends_are_enabled/0]).
 | 
				
			||||||
-export([check_user_pass_login/2, check_user_login/2, check_user_login/3, check_user_loopback/2,
 | 
					-export([check_user_pass_login/2, check_user_login/2, check_user_login/3, check_user_loopback/2,
 | 
				
			||||||
         check_vhost_access/4, check_resource_access/4, check_topic_access/4,
 | 
					         check_vhost_access/4, check_resource_access/4, check_topic_access/4,
 | 
				
			||||||
         check_user_id/2]).
 | 
					         check_user_id/2]).
 | 
				
			||||||
| 
						 | 
					@ -18,6 +19,141 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%%----------------------------------------------------------------------------
 | 
					%%----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-spec ensure_auth_backends_are_enabled() -> Ret when
 | 
				
			||||||
 | 
					      Ret :: ok | {error, Reason},
 | 
				
			||||||
 | 
					      Reason :: string().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ensure_auth_backends_are_enabled() ->
 | 
				
			||||||
 | 
					    {ok, AuthBackends} = application:get_env(rabbit, auth_backends),
 | 
				
			||||||
 | 
					    ValidAuthBackends = filter_valid_auth_backend_configuration(
 | 
				
			||||||
 | 
					                          AuthBackends, []),
 | 
				
			||||||
 | 
					    case ValidAuthBackends of
 | 
				
			||||||
 | 
					        AuthBackends ->
 | 
				
			||||||
 | 
					            ok;
 | 
				
			||||||
 | 
					        [_ | _] ->
 | 
				
			||||||
 | 
					            %% Some auth backend modules were filtered out because their
 | 
				
			||||||
 | 
					            %% corresponding plugin is either unavailable or disabled. We
 | 
				
			||||||
 | 
					            %% update the application environment variable so that
 | 
				
			||||||
 | 
					            %% authentication and authorization do not try to use them.
 | 
				
			||||||
 | 
					            ?LOG_WARNING(
 | 
				
			||||||
 | 
					               "Some configured backends were dropped because their "
 | 
				
			||||||
 | 
					               "corresponding plugins are disabled. Please look at the "
 | 
				
			||||||
 | 
					               "info messages above to learn which plugin(s) should be "
 | 
				
			||||||
 | 
					               "enabled. Here is the list of auth backends kept after "
 | 
				
			||||||
 | 
					               "filering:~n~p", [ValidAuthBackends]),
 | 
				
			||||||
 | 
					            ok = application:set_env(rabbit, auth_backends, ValidAuthBackends),
 | 
				
			||||||
 | 
					            ok;
 | 
				
			||||||
 | 
					        [] ->
 | 
				
			||||||
 | 
					            %% None of the auth backend modules are usable. Log an error and
 | 
				
			||||||
 | 
					            %% abort the boot of RabbitMQ.
 | 
				
			||||||
 | 
					            ?LOG_ERROR(
 | 
				
			||||||
 | 
					               "None of the configured auth backends are usable because "
 | 
				
			||||||
 | 
					               "their corresponding plugins were not enabled. Please look "
 | 
				
			||||||
 | 
					               "at the info messages above to learn which plugin(s) should "
 | 
				
			||||||
 | 
					               "be enabled."),
 | 
				
			||||||
 | 
					            {error,
 | 
				
			||||||
 | 
					             "Authentication/authorization backends require plugins to be "
 | 
				
			||||||
 | 
					             "enabled; see logs for details"}
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filter_valid_auth_backend_configuration(
 | 
				
			||||||
 | 
					  [Mod | Rest], ValidAuthBackends)
 | 
				
			||||||
 | 
					  when is_atom(Mod) ->
 | 
				
			||||||
 | 
					    case is_auth_backend_module_enabled(Mod) of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            ValidAuthBackends1 = [Mod | ValidAuthBackends],
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends1);
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends)
 | 
				
			||||||
 | 
					    end;
 | 
				
			||||||
 | 
					filter_valid_auth_backend_configuration(
 | 
				
			||||||
 | 
					  [{ModN, ModZ} = Mod | Rest], ValidAuthBackends)
 | 
				
			||||||
 | 
					  when is_atom(ModN) andalso is_atom(ModZ) ->
 | 
				
			||||||
 | 
					    %% Both auth backend modules must be usable to keep the entire pair.
 | 
				
			||||||
 | 
					    IsModNEnabled = is_auth_backend_module_enabled(ModN),
 | 
				
			||||||
 | 
					    IsModZEnabled = is_auth_backend_module_enabled(ModZ),
 | 
				
			||||||
 | 
					    case IsModNEnabled andalso IsModZEnabled of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            ValidAuthBackends1 = [Mod | ValidAuthBackends],
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends1);
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends)
 | 
				
			||||||
 | 
					    end;
 | 
				
			||||||
 | 
					filter_valid_auth_backend_configuration(
 | 
				
			||||||
 | 
					  [{ModN, ModZs} | Rest], ValidAuthBackends)
 | 
				
			||||||
 | 
					  when is_atom(ModN) andalso is_list(ModZs) ->
 | 
				
			||||||
 | 
					    %% The authentication backend module and at least on of the authorization
 | 
				
			||||||
 | 
					    %% backend module must be usable to keep the entire pair.
 | 
				
			||||||
 | 
					    %%
 | 
				
			||||||
 | 
					    %% The list of authorization backend modules may be shorter than the
 | 
				
			||||||
 | 
					    %% configured one after the filtering.
 | 
				
			||||||
 | 
					    IsModNEnabled = is_auth_backend_module_enabled(ModN),
 | 
				
			||||||
 | 
					    EnabledModZs = lists:filter(fun is_auth_backend_module_enabled/1, ModZs),
 | 
				
			||||||
 | 
					    case IsModNEnabled andalso EnabledModZs =/= [] of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            Mod1 = {ModN, EnabledModZs},
 | 
				
			||||||
 | 
					            ValidAuthBackends1 = [Mod1 | ValidAuthBackends],
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends1);
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            filter_valid_auth_backend_configuration(Rest, ValidAuthBackends)
 | 
				
			||||||
 | 
					    end;
 | 
				
			||||||
 | 
					filter_valid_auth_backend_configuration([], ValidAuthBackends) ->
 | 
				
			||||||
 | 
					    lists:reverse(ValidAuthBackends).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					is_auth_backend_module_enabled(Mod) when is_atom(Mod) ->
 | 
				
			||||||
 | 
					    %% We check if the module is provided by the core of RabbitMQ or a plugin,
 | 
				
			||||||
 | 
					    %% and if that plugin is enabled.
 | 
				
			||||||
 | 
					    {ok, Modules} = application:get_key(rabbit, modules),
 | 
				
			||||||
 | 
					    case lists:member(Mod, Modules) of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            true;
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            %% The module is not provided by RabbitMQ core. Let's query
 | 
				
			||||||
 | 
					            %% plugins then.
 | 
				
			||||||
 | 
					            case rabbit_plugins:which_plugin(Mod) of
 | 
				
			||||||
 | 
					                {ok, PluginName} ->
 | 
				
			||||||
 | 
					                    %% FIXME: The definition of an "enabled plugin" in
 | 
				
			||||||
 | 
					                    %% `rabbit_plugins' varies from funtion to function.
 | 
				
			||||||
 | 
					                    %% Sometimes, it means the "rabbitmq-plugin enable
 | 
				
			||||||
 | 
					                    %% <plugin>" was executed, sometimes it means the plugin
 | 
				
			||||||
 | 
					                    %% is running.
 | 
				
			||||||
 | 
					                    %%
 | 
				
			||||||
 | 
					                    %% This function is a boot step and is executed before
 | 
				
			||||||
 | 
					                    %% plugin are started. Therefore, we can't rely on
 | 
				
			||||||
 | 
					                    %% `rabbit_plugins:is_enabled/1' because it uses the
 | 
				
			||||||
 | 
					                    %% latter definition of "the plugin is running, regardless
 | 
				
			||||||
 | 
					                    %% of if it is enabled or not".
 | 
				
			||||||
 | 
					                    %%
 | 
				
			||||||
 | 
					                    %% Therefore, we use `rabbit_plugins:enabled_plugins/0'
 | 
				
			||||||
 | 
					                    %% which lists explicitly enabled plugins. Unfortunately,
 | 
				
			||||||
 | 
					                    %% it won't include the implicitly enabled plugins (i.e,
 | 
				
			||||||
 | 
					                    %% plugins that are dependencies of explicitly enabled
 | 
				
			||||||
 | 
					                    %% plugins).
 | 
				
			||||||
 | 
					                    EnabledPlugins = rabbit_plugins:enabled_plugins(),
 | 
				
			||||||
 | 
					                    case lists:member(PluginName, EnabledPlugins) of
 | 
				
			||||||
 | 
					                        true ->
 | 
				
			||||||
 | 
					                            true;
 | 
				
			||||||
 | 
					                        false ->
 | 
				
			||||||
 | 
					                            ?LOG_INFO(
 | 
				
			||||||
 | 
					                               "The `~ts` auth backend module is configured. "
 | 
				
			||||||
 | 
					                               "However, the `~ts` plugin must be enabled in "
 | 
				
			||||||
 | 
					                               "order to use this auth backend. Until then "
 | 
				
			||||||
 | 
					                               "it will be skipped during "
 | 
				
			||||||
 | 
					                               "authentication/authorization",
 | 
				
			||||||
 | 
					                               [Mod, PluginName]),
 | 
				
			||||||
 | 
					                            false
 | 
				
			||||||
 | 
					                    end;
 | 
				
			||||||
 | 
					                {error, no_provider} ->
 | 
				
			||||||
 | 
					                    ?LOG_INFO(
 | 
				
			||||||
 | 
					                       "The `~ts` auth backend module is configured. "
 | 
				
			||||||
 | 
					                       "However, no plugins available provide this "
 | 
				
			||||||
 | 
					                       "module. Until then it will be skipped during "
 | 
				
			||||||
 | 
					                       "authentication/authorization",
 | 
				
			||||||
 | 
					                       [Mod]),
 | 
				
			||||||
 | 
					                    false
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-spec check_user_pass_login
 | 
					-spec check_user_pass_login
 | 
				
			||||||
        (rabbit_types:username(), rabbit_types:password()) ->
 | 
					        (rabbit_types:username(), rabbit_types:password()) ->
 | 
				
			||||||
            {'ok', rabbit_types:user()} |
 | 
					            {'ok', rabbit_types:user()} |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,12 +8,13 @@
 | 
				
			||||||
-module(rabbit_plugins).
 | 
					-module(rabbit_plugins).
 | 
				
			||||||
-include_lib("rabbit_common/include/rabbit.hrl").
 | 
					-include_lib("rabbit_common/include/rabbit.hrl").
 | 
				
			||||||
-include_lib("kernel/include/logger.hrl").
 | 
					-include_lib("kernel/include/logger.hrl").
 | 
				
			||||||
-export([setup/0, active/0, read_enabled/1, list/1, list/2, dependencies/3, running_plugins/0]).
 | 
					-export([setup/0, active/0, read_enabled/1, list/0, list/1, list/2, dependencies/3, running_plugins/0]).
 | 
				
			||||||
-export([ensure/1]).
 | 
					-export([ensure/1]).
 | 
				
			||||||
-export([validate_plugins/1, format_invalid_plugins/1]).
 | 
					-export([validate_plugins/1, format_invalid_plugins/1]).
 | 
				
			||||||
-export([is_strictly_plugin/1, strictly_plugins/2, strictly_plugins/1]).
 | 
					-export([is_strictly_plugin/1, strictly_plugins/2, strictly_plugins/1]).
 | 
				
			||||||
-export([plugins_dir/0, plugin_names/1, plugins_expand_dir/0, enabled_plugins_file/0]).
 | 
					-export([plugins_dir/0, plugin_names/1, plugins_expand_dir/0, enabled_plugins_file/0]).
 | 
				
			||||||
-export([is_enabled/1, is_enabled_on_node/2]).
 | 
					-export([is_enabled/1, is_enabled_on_node/2, enabled_plugins/0]).
 | 
				
			||||||
 | 
					-export([which_plugin/1]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
% Export for testing purpose.
 | 
					% Export for testing purpose.
 | 
				
			||||||
-export([is_version_supported/2, validate_plugins/2]).
 | 
					-export([is_version_supported/2, validate_plugins/2]).
 | 
				
			||||||
| 
						 | 
					@ -130,7 +131,7 @@ setup() ->
 | 
				
			||||||
-spec active() -> [plugin_name()].
 | 
					-spec active() -> [plugin_name()].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
active() ->
 | 
					active() ->
 | 
				
			||||||
    InstalledPlugins = plugin_names(list(plugins_dir())),
 | 
					    InstalledPlugins = plugin_names(list()),
 | 
				
			||||||
    [App || {App, _, _} <- rabbit_misc:which_applications(),
 | 
					    [App || {App, _, _} <- rabbit_misc:which_applications(),
 | 
				
			||||||
            lists:member(App, InstalledPlugins)].
 | 
					            lists:member(App, InstalledPlugins)].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,6 +158,13 @@ is_enabled_on_node(Name, Node) ->
 | 
				
			||||||
        _Class:_Reason:_Stacktrace -> false
 | 
					        _Class:_Reason:_Stacktrace -> false
 | 
				
			||||||
    end.
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-spec list() -> [#plugin{}].
 | 
				
			||||||
 | 
					%% @doc Get the list of plugins from the configured plugin path.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					list() ->
 | 
				
			||||||
 | 
					    PluginsPath = plugins_dir(),
 | 
				
			||||||
 | 
					    list(PluginsPath).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%% @doc Get the list of plugins which are ready to be enabled.
 | 
					%% @doc Get the list of plugins which are ready to be enabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-spec list(string()) -> [#plugin{}].
 | 
					-spec list(string()) -> [#plugin{}].
 | 
				
			||||||
| 
						 | 
					@ -228,7 +236,7 @@ strictly_plugins(Plugins, AllPlugins) ->
 | 
				
			||||||
-spec strictly_plugins([plugin_name()]) -> [plugin_name()].
 | 
					-spec strictly_plugins([plugin_name()]) -> [plugin_name()].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
strictly_plugins(Plugins) ->
 | 
					strictly_plugins(Plugins) ->
 | 
				
			||||||
    AllPlugins = list(plugins_dir()),
 | 
					    AllPlugins = list(),
 | 
				
			||||||
    lists:filter(
 | 
					    lists:filter(
 | 
				
			||||||
      fun(Name) ->
 | 
					      fun(Name) ->
 | 
				
			||||||
              is_strictly_plugin(lists:keyfind(Name, #plugin.name, AllPlugins))
 | 
					              is_strictly_plugin(lists:keyfind(Name, #plugin.name, AllPlugins))
 | 
				
			||||||
| 
						 | 
					@ -279,11 +287,61 @@ running_plugins() ->
 | 
				
			||||||
    ActivePlugins = active(),
 | 
					    ActivePlugins = active(),
 | 
				
			||||||
    {ok, [{App, Vsn} || {App, _ , Vsn} <- rabbit_misc:which_applications(), lists:member(App, ActivePlugins)]}.
 | 
					    {ok, [{App, Vsn} || {App, _ , Vsn} <- rabbit_misc:which_applications(), lists:member(App, ActivePlugins)]}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-spec which_plugin(Module) -> Ret when
 | 
				
			||||||
 | 
					      Module :: module(),
 | 
				
			||||||
 | 
					      Ret :: {ok, PluginName} | {error, Reason},
 | 
				
			||||||
 | 
					      PluginName :: atom(),
 | 
				
			||||||
 | 
					      Reason :: no_provider.
 | 
				
			||||||
 | 
					%% @doc Returns the name of the plugin that provides the given module.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%% If no plugin provides the module, `{error, no_provider}' is returned.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%% The returned plugin might not be enabled, thus using the given module might
 | 
				
			||||||
 | 
					%% not work until the plugin is enabled.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%% @returns An `{ok, PluginName}' tuple with the name of the plugin providing
 | 
				
			||||||
 | 
					%% the module, or `{error, no_provider}'.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					which_plugin(Module) ->
 | 
				
			||||||
 | 
					    Plugins = list(),
 | 
				
			||||||
 | 
					    which_plugin(Plugins, Module).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					which_plugin([#plugin{name = Name} | Rest], Module) ->
 | 
				
			||||||
 | 
					    %% Get the list of modules belonging to this plugin.
 | 
				
			||||||
 | 
					    ModulesKey = case application:get_key(Name, modules) of
 | 
				
			||||||
 | 
					                     {ok, _} = Ret ->
 | 
				
			||||||
 | 
					                         Ret;
 | 
				
			||||||
 | 
					                     undefined ->
 | 
				
			||||||
 | 
					                         %% The plugin application might not be loaded. Load
 | 
				
			||||||
 | 
					                         %% it temporarily and try again.
 | 
				
			||||||
 | 
					                         case application:load(Name) of
 | 
				
			||||||
 | 
					                             ok ->
 | 
				
			||||||
 | 
					                                 Ret = application:get_key(Name, modules),
 | 
				
			||||||
 | 
					                                 _ = application:unload(Name),
 | 
				
			||||||
 | 
					                                 Ret;
 | 
				
			||||||
 | 
					                             {error, _Reason} ->
 | 
				
			||||||
 | 
					                                 undefined
 | 
				
			||||||
 | 
					                         end
 | 
				
			||||||
 | 
					                 end,
 | 
				
			||||||
 | 
					    case ModulesKey of
 | 
				
			||||||
 | 
					        {ok, Modules} ->
 | 
				
			||||||
 | 
					            case lists:member(Module, Modules) of
 | 
				
			||||||
 | 
					                true ->
 | 
				
			||||||
 | 
					                    {ok, Name};
 | 
				
			||||||
 | 
					                false ->
 | 
				
			||||||
 | 
					                    which_plugin(Rest, Module)
 | 
				
			||||||
 | 
					            end;
 | 
				
			||||||
 | 
					        undefined ->
 | 
				
			||||||
 | 
					            which_plugin(Rest, Module)
 | 
				
			||||||
 | 
					    end;
 | 
				
			||||||
 | 
					which_plugin([], _Module) ->
 | 
				
			||||||
 | 
					    {error, no_provider}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%%----------------------------------------------------------------------------
 | 
					%%----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
prepare_plugins(Enabled) ->
 | 
					prepare_plugins(Enabled) ->
 | 
				
			||||||
    ExpandDir = plugins_expand_dir(),
 | 
					    ExpandDir = plugins_expand_dir(),
 | 
				
			||||||
    AllPlugins = list(plugins_dir()),
 | 
					    AllPlugins = list(),
 | 
				
			||||||
    Wanted = dependencies(false, Enabled, AllPlugins),
 | 
					    Wanted = dependencies(false, Enabled, AllPlugins),
 | 
				
			||||||
    WantedPlugins = lookup_plugins(Wanted, AllPlugins),
 | 
					    WantedPlugins = lookup_plugins(Wanted, AllPlugins),
 | 
				
			||||||
    {ValidPlugins, Problems} = validate_plugins(WantedPlugins),
 | 
					    {ValidPlugins, Problems} = validate_plugins(WantedPlugins),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,34 +2,54 @@
 | 
				
			||||||
%% License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					%% License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
 | 
					%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
 | 
				
			||||||
%%
 | 
					%%
 | 
				
			||||||
%% Copyright (c) 2024 Broadcom. All Rights Reserved.
 | 
					%% Copyright (c) 2024-2025 Broadcom. All Rights Reserved.
 | 
				
			||||||
%% The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
 | 
					%% The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
 | 
				
			||||||
%%
 | 
					%%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-module(rabbit_access_control_SUITE).
 | 
					-module(rabbit_access_control_SUITE).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-compile(export_all).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-include_lib("eunit/include/eunit.hrl").
 | 
					-include_lib("eunit/include/eunit.hrl").
 | 
				
			||||||
 | 
					-include_lib("common_test/include/ct.hrl").
 | 
				
			||||||
 | 
					-include_lib("rabbitmq_ct_helpers/include/rabbit_assert.hrl").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-include_lib("rabbit_common/include/rabbit.hrl").
 | 
					-include_lib("rabbit_common/include/rabbit.hrl").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-export([all/0,
 | 
				
			||||||
 | 
					         groups/0,
 | 
				
			||||||
 | 
					         init_per_suite/1,
 | 
				
			||||||
 | 
					         end_per_suite/1,
 | 
				
			||||||
 | 
					         init_per_group/2,
 | 
				
			||||||
 | 
					         end_per_group/2,
 | 
				
			||||||
 | 
					         init_per_testcase/2,
 | 
				
			||||||
 | 
					         end_per_testcase/2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         expiry_timestamp/1,
 | 
				
			||||||
 | 
					         with_enabled_plugin/1,
 | 
				
			||||||
 | 
					         with_enabled_plugin_plus_internal/1,
 | 
				
			||||||
 | 
					         with_missing_plugin/1,
 | 
				
			||||||
 | 
					         with_missing_plugin_plus_internal/1,
 | 
				
			||||||
 | 
					         with_disabled_plugin/1,
 | 
				
			||||||
 | 
					         with_disabled_plugin_plus_internal/1
 | 
				
			||||||
 | 
					        ]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%%%===================================================================
 | 
					%%%===================================================================
 | 
				
			||||||
%%% Common Test callbacks
 | 
					%%% Common Test callbacks
 | 
				
			||||||
%%%===================================================================
 | 
					%%%===================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
all() ->
 | 
					all() ->
 | 
				
			||||||
    [{group, tests}].
 | 
					    [{group, unit_tests},
 | 
				
			||||||
 | 
					     {group, integration_tests}].
 | 
				
			||||||
%% replicate eunit like test resolution
 | 
					 | 
				
			||||||
all_tests() ->
 | 
					 | 
				
			||||||
    [F
 | 
					 | 
				
			||||||
     || {F, _} <- ?MODULE:module_info(functions),
 | 
					 | 
				
			||||||
        re:run(atom_to_list(F), "_test$") /= nomatch].
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
groups() ->
 | 
					groups() ->
 | 
				
			||||||
    [{tests, [], all_tests()}].
 | 
					    [{unit_tests, [], [expiry_timestamp]},
 | 
				
			||||||
 | 
					     {integration_tests, [], [with_enabled_plugin,
 | 
				
			||||||
 | 
					                              with_enabled_plugin_plus_internal,
 | 
				
			||||||
 | 
					                              with_missing_plugin,
 | 
				
			||||||
 | 
					                              with_missing_plugin_plus_internal,
 | 
				
			||||||
 | 
					                              with_disabled_plugin,
 | 
				
			||||||
 | 
					                              with_disabled_plugin_plus_internal]}].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
init_per_suite(Config) ->
 | 
					init_per_suite(Config) ->
 | 
				
			||||||
    Config.
 | 
					    Config.
 | 
				
			||||||
| 
						 | 
					@ -37,20 +57,74 @@ init_per_suite(Config) ->
 | 
				
			||||||
end_per_suite(_Config) ->
 | 
					end_per_suite(_Config) ->
 | 
				
			||||||
    ok.
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init_per_group(integration_tests, Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:log_environment(),
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:run_setup_steps(Config);
 | 
				
			||||||
init_per_group(_Group, Config) ->
 | 
					init_per_group(_Group, Config) ->
 | 
				
			||||||
    Config.
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end_per_group(integration_tests, Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:run_teardown_steps(Config);
 | 
				
			||||||
end_per_group(_Group, _Config) ->
 | 
					end_per_group(_Group, _Config) ->
 | 
				
			||||||
    ok.
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init_per_testcase(Testcase, Config)
 | 
				
			||||||
 | 
					  when Testcase =:= with_missing_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_missing_plugin_plus_internal ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_started(Config, Testcase),
 | 
				
			||||||
 | 
					    do_init_per_testcase(Testcase, Config, []);
 | 
				
			||||||
 | 
					init_per_testcase(Testcase, Config)
 | 
				
			||||||
 | 
					  when Testcase =:= with_enabled_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_enabled_plugin_plus_internal orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_disabled_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_disabled_plugin_plus_internal ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_started(Config, Testcase),
 | 
				
			||||||
 | 
					    do_init_per_testcase(Testcase, Config, [fun prepare_my_plugin/1]);
 | 
				
			||||||
init_per_testcase(_TestCase, Config) ->
 | 
					init_per_testcase(_TestCase, Config) ->
 | 
				
			||||||
    Config.
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_init_per_testcase(Testcase, Config, PrepSteps) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_started(Config, Testcase),
 | 
				
			||||||
 | 
					    TestNumber = rabbit_ct_helpers:testcase_number(Config, ?MODULE, Testcase),
 | 
				
			||||||
 | 
					    ClusterSize = 1,
 | 
				
			||||||
 | 
					    Config1 = rabbit_ct_helpers:set_config(
 | 
				
			||||||
 | 
					                Config,
 | 
				
			||||||
 | 
					                [{rmq_nodename_suffix, Testcase},
 | 
				
			||||||
 | 
					                 {rmq_nodes_count, ClusterSize},
 | 
				
			||||||
 | 
					                 {tcp_ports_base, {skip_n_nodes, TestNumber * ClusterSize}},
 | 
				
			||||||
 | 
					                 {start_rmq_with_plugins_disabled, true}]),
 | 
				
			||||||
 | 
					    Config2 = rabbit_ct_helpers:merge_app_env(
 | 
				
			||||||
 | 
					                Config1,
 | 
				
			||||||
 | 
					                {rabbit,
 | 
				
			||||||
 | 
					                 [{log, [{file, [{level, debug}]}]}]}),
 | 
				
			||||||
 | 
					    Config3 = rabbit_ct_helpers:run_steps(
 | 
				
			||||||
 | 
					                Config2,
 | 
				
			||||||
 | 
					                PrepSteps ++
 | 
				
			||||||
 | 
					                rabbit_ct_broker_helpers:setup_steps() ++
 | 
				
			||||||
 | 
					                rabbit_ct_client_helpers:setup_steps()),
 | 
				
			||||||
 | 
					    Config3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end_per_testcase(Testcase, Config)
 | 
				
			||||||
 | 
					  when Testcase =:= with_enabled_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_enabled_plugin_plus_internal orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_missing_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_missing_plugin_plus_internal orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_disabled_plugin orelse
 | 
				
			||||||
 | 
					       Testcase =:= with_disabled_plugin_plus_internal ->
 | 
				
			||||||
 | 
					    Config1 = rabbit_ct_helpers:run_steps(
 | 
				
			||||||
 | 
					                Config,
 | 
				
			||||||
 | 
					                rabbit_ct_client_helpers:teardown_steps() ++
 | 
				
			||||||
 | 
					                rabbit_ct_broker_helpers:teardown_steps()),
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_finished(Config1, Testcase);
 | 
				
			||||||
end_per_testcase(_TestCase, _Config) ->
 | 
					end_per_testcase(_TestCase, _Config) ->
 | 
				
			||||||
    meck:unload(),
 | 
					    meck:unload(),
 | 
				
			||||||
    ok.
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
expiry_timestamp_test(_) ->
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					%% Testcases.
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expiry_timestamp(_) ->
 | 
				
			||||||
    %% test rabbit_access_control:expiry_timestamp/1 returns the earliest expiry time
 | 
					    %% test rabbit_access_control:expiry_timestamp/1 returns the earliest expiry time
 | 
				
			||||||
    Now = os:system_time(seconds),
 | 
					    Now = os:system_time(seconds),
 | 
				
			||||||
    BeforeNow = Now - 60,
 | 
					    BeforeNow = Now - 60,
 | 
				
			||||||
| 
						 | 
					@ -102,3 +176,265 @@ expiry_timestamp_test(_) ->
 | 
				
			||||||
                                    {rabbit_expiry_backend, unused}]},
 | 
					                                    {rabbit_expiry_backend, unused}]},
 | 
				
			||||||
    ?assertEqual(Now, rabbit_access_control:expiry_timestamp(User7)),
 | 
					    ?assertEqual(Now, rabbit_access_control:expiry_timestamp(User7)),
 | 
				
			||||||
    ok.
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_enabled_plugin(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    ok = rabbit_ct_broker_helpers:enable_plugin(Config, Node, my_auth_plugin),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, os, unsetenv, ["RABBITMQ_ENABLED_PLUGINS"]),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, os, unsetenv, ["LEAVE_PLUGINS_DISABLED"]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(
 | 
				
			||||||
 | 
					       ok,
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertMatch({error, {auth_failure, _}}, test_connection(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_enabled_plugin_plus_internal(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    ok = rabbit_ct_broker_helpers:enable_plugin(Config, Node, my_auth_plugin),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, os, unsetenv, ["RABBITMQ_ENABLED_PLUGINS"]),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, os, unsetenv, ["LEAVE_PLUGINS_DISABLED"]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin, rabbit_auth_backend_internal],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(
 | 
				
			||||||
 | 
					       ok,
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(ok, test_connection(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_missing_plugin(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertThrow(
 | 
				
			||||||
 | 
					       {error,
 | 
				
			||||||
 | 
					        {rabbit,
 | 
				
			||||||
 | 
					         {{error,
 | 
				
			||||||
 | 
					           "Authentication/authorization backends require plugins to be "
 | 
				
			||||||
 | 
					           "enabled; see logs for details"},
 | 
				
			||||||
 | 
					          _}}},
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?awaitMatch(
 | 
				
			||||||
 | 
					       true,
 | 
				
			||||||
 | 
					       check_log(Config, Node, "no plugins available provide this module"),
 | 
				
			||||||
 | 
					       30000),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_missing_plugin_plus_internal(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin, rabbit_auth_backend_internal],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(
 | 
				
			||||||
 | 
					       ok,
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?awaitMatch(
 | 
				
			||||||
 | 
					       true,
 | 
				
			||||||
 | 
					       check_log(Config, Node, "no plugins available provide this module"),
 | 
				
			||||||
 | 
					       30000),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(ok, test_connection(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_disabled_plugin(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertThrow(
 | 
				
			||||||
 | 
					       {error,
 | 
				
			||||||
 | 
					        {rabbit,
 | 
				
			||||||
 | 
					         {{error,
 | 
				
			||||||
 | 
					           "Authentication/authorization backends require plugins to be "
 | 
				
			||||||
 | 
					           "enabled; see logs for details"},
 | 
				
			||||||
 | 
					          _}}},
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?awaitMatch(
 | 
				
			||||||
 | 
					       true,
 | 
				
			||||||
 | 
					       check_log(
 | 
				
			||||||
 | 
					         Config, Node,
 | 
				
			||||||
 | 
					         "the `my_auth_plugin` plugin must be enabled in order to use "
 | 
				
			||||||
 | 
					         "this auth backend"),
 | 
				
			||||||
 | 
					       30000),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with_disabled_plugin_plus_internal(Config) ->
 | 
				
			||||||
 | 
					    Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:stop_broker(Config, Node),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AuthBackends = [my_auth_plugin, rabbit_auth_backend_internal],
 | 
				
			||||||
 | 
					    rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					      Config, Node, application, set_env,
 | 
				
			||||||
 | 
					      [rabbit, auth_backends, AuthBackends, [{persistent, true}]]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(
 | 
				
			||||||
 | 
					       ok,
 | 
				
			||||||
 | 
					       rabbit_ct_broker_helpers:start_broker(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?awaitMatch(
 | 
				
			||||||
 | 
					       true,
 | 
				
			||||||
 | 
					       check_log(
 | 
				
			||||||
 | 
					         Config, Node,
 | 
				
			||||||
 | 
					         "the `my_auth_plugin` plugin must be enabled in order to use "
 | 
				
			||||||
 | 
					         "this auth backend"),
 | 
				
			||||||
 | 
					       30000),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ?assertEqual(ok, test_connection(Config, Node)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					%% Internal helpers.
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					prepare_my_plugin(Config) ->
 | 
				
			||||||
 | 
					    case os:getenv("RABBITMQ_RUN") of
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            build_my_plugin(Config);
 | 
				
			||||||
 | 
					        _ ->
 | 
				
			||||||
 | 
					            MyPluginDir = filename:dirname(
 | 
				
			||||||
 | 
					                            filename:dirname(
 | 
				
			||||||
 | 
					                              code:where_is_file("my_auth_plugin.app"))),
 | 
				
			||||||
 | 
					            PluginsDir = filename:dirname(MyPluginDir),
 | 
				
			||||||
 | 
					            rabbit_ct_helpers:set_config(
 | 
				
			||||||
 | 
					              Config, [{rmq_plugins_dir, PluginsDir}])
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build_my_plugin(Config) ->
 | 
				
			||||||
 | 
					    DataDir = filename:join(
 | 
				
			||||||
 | 
					                filename:dirname(filename:dirname(?config(data_dir, Config))),
 | 
				
			||||||
 | 
					                ?MODULE_STRING ++ "_data"),
 | 
				
			||||||
 | 
					    PluginSrcDir = filename:join(DataDir, "my_auth_plugin"),
 | 
				
			||||||
 | 
					    PluginsDir = filename:join(PluginSrcDir, "plugins"),
 | 
				
			||||||
 | 
					    Config1 = rabbit_ct_helpers:set_config(Config,
 | 
				
			||||||
 | 
					                                           [{rmq_plugins_dir, PluginsDir}]),
 | 
				
			||||||
 | 
					    {MyPlugin, OtherPlugins} = list_my_plugin_plugins(PluginSrcDir),
 | 
				
			||||||
 | 
					    case MyPlugin of
 | 
				
			||||||
 | 
					        [] ->
 | 
				
			||||||
 | 
					            DepsDir = ?config(erlang_mk_depsdir, Config),
 | 
				
			||||||
 | 
					            Args = ["test-dist",
 | 
				
			||||||
 | 
					                    {"DEPS_DIR=~ts", [DepsDir]},
 | 
				
			||||||
 | 
					                    %% We clear ALL_DEPS_DIRS to make sure they are
 | 
				
			||||||
 | 
					                    %% not recompiled when the plugin is built. `rabbit`
 | 
				
			||||||
 | 
					                    %% was previously compiled with -DTEST and if it is
 | 
				
			||||||
 | 
					                    %% recompiled because of this plugin, it will be
 | 
				
			||||||
 | 
					                    %% recompiled without -DTEST: the testsuite depends
 | 
				
			||||||
 | 
					                    %% on test code so we can't allow that.
 | 
				
			||||||
 | 
					                    %%
 | 
				
			||||||
 | 
					                    %% Note that we do not clear the DEPS variable: we need
 | 
				
			||||||
 | 
					                    %% it to be correct because it is used to generate
 | 
				
			||||||
 | 
					                    %% `my_auth_plugin.app` (and a RabbitMQ plugin must
 | 
				
			||||||
 | 
					                    %% depend on `rabbit`).
 | 
				
			||||||
 | 
					                    "ALL_DEPS_DIRS="],
 | 
				
			||||||
 | 
					            case rabbit_ct_helpers:make(Config1, PluginSrcDir, Args) of
 | 
				
			||||||
 | 
					                {ok, _} ->
 | 
				
			||||||
 | 
					                    {_, OtherPlugins1} = list_my_plugin_plugins(PluginSrcDir),
 | 
				
			||||||
 | 
					                    remove_other_plugins(PluginSrcDir, OtherPlugins1),
 | 
				
			||||||
 | 
					                    update_cli_path(Config1, PluginSrcDir);
 | 
				
			||||||
 | 
					                {error, _} ->
 | 
				
			||||||
 | 
					                    {skip,
 | 
				
			||||||
 | 
					                     "Failed to compile the `my_auth_plugin` test plugin"}
 | 
				
			||||||
 | 
					            end;
 | 
				
			||||||
 | 
					        _ ->
 | 
				
			||||||
 | 
					            remove_other_plugins(PluginSrcDir, OtherPlugins),
 | 
				
			||||||
 | 
					            update_cli_path(Config1, PluginSrcDir)
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					update_cli_path(Config, PluginSrcDir) ->
 | 
				
			||||||
 | 
					    SbinDir = filename:join(PluginSrcDir, "sbin"),
 | 
				
			||||||
 | 
					    Rabbitmqctl = filename:join(SbinDir, "rabbitmqctl"),
 | 
				
			||||||
 | 
					    RabbitmqPlugins = filename:join(SbinDir, "rabbitmq-plugins"),
 | 
				
			||||||
 | 
					    RabbitmqQueues = filename:join(SbinDir, "rabbitmq-queues"),
 | 
				
			||||||
 | 
					    case filelib:is_regular(Rabbitmqctl) of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            ct:pal(?LOW_IMPORTANCE,
 | 
				
			||||||
 | 
					                   "Switching to CLI in e.g. ~ts", [Rabbitmqctl]),
 | 
				
			||||||
 | 
					            rabbit_ct_helpers:set_config(
 | 
				
			||||||
 | 
					              Config,
 | 
				
			||||||
 | 
					              [{rabbitmqctl_cmd, Rabbitmqctl},
 | 
				
			||||||
 | 
					               {rabbitmq_plugins_cmd, RabbitmqPlugins},
 | 
				
			||||||
 | 
					               {rabbitmq_queues_cmd, RabbitmqQueues}]);
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            Config
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					list_my_plugin_plugins(PluginSrcDir) ->
 | 
				
			||||||
 | 
					    Files = filelib:wildcard("plugins/*", PluginSrcDir),
 | 
				
			||||||
 | 
					    lists:partition(
 | 
				
			||||||
 | 
					      fun(Path) ->
 | 
				
			||||||
 | 
					              Filename = filename:basename(Path),
 | 
				
			||||||
 | 
					              re:run(Filename, "^my_auth_plugin-", [{capture, none}]) =:= match
 | 
				
			||||||
 | 
					      end, Files).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					remove_other_plugins(PluginSrcDir, OtherPlugins) ->
 | 
				
			||||||
 | 
					    ok = rabbit_file:recursive_delete(
 | 
				
			||||||
 | 
					           [filename:join(PluginSrcDir, OtherPlugin)
 | 
				
			||||||
 | 
					            || OtherPlugin <- OtherPlugins]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					check_log(Config, Node, Msg) ->
 | 
				
			||||||
 | 
					    LogLocations = rabbit_ct_broker_helpers:rpc(
 | 
				
			||||||
 | 
					                     Config, Node,
 | 
				
			||||||
 | 
					                     rabbit, log_locations, []),
 | 
				
			||||||
 | 
					    lists:any(
 | 
				
			||||||
 | 
					      fun(LogLocation) ->
 | 
				
			||||||
 | 
					              check_log1(LogLocation, Msg)
 | 
				
			||||||
 | 
					      end, LogLocations).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					check_log1(LogLocation, Msg) ->
 | 
				
			||||||
 | 
					    case filelib:is_regular(LogLocation) of
 | 
				
			||||||
 | 
					        true ->
 | 
				
			||||||
 | 
					            {ok, Content} = file:read_file(LogLocation),
 | 
				
			||||||
 | 
					            ReOpts = [{capture, none}, multiline, unicode],
 | 
				
			||||||
 | 
					            match =:= re:run(Content, Msg, ReOpts);
 | 
				
			||||||
 | 
					        false ->
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test_connection(Config, Node) ->
 | 
				
			||||||
 | 
					    case rabbit_ct_client_helpers:open_unmanaged_connection(Config, Node) of
 | 
				
			||||||
 | 
					        Conn when is_pid(Conn) ->
 | 
				
			||||||
 | 
					            ok = rabbit_ct_client_helpers:close_connection(Conn),
 | 
				
			||||||
 | 
					            ok;
 | 
				
			||||||
 | 
					        {error, _} = Error ->
 | 
				
			||||||
 | 
					            Error
 | 
				
			||||||
 | 
					    end.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					PROJECT = my_auth_plugin
 | 
				
			||||||
 | 
					PROJECT_DESCRIPTION = Plugin to test access control
 | 
				
			||||||
 | 
					PROJECT_VERSION = 1.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					define PROJECT_APP_EXTRA_KEYS
 | 
				
			||||||
 | 
						{broker_version_requirements, []}
 | 
				
			||||||
 | 
					endef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPS = rabbit_common rabbit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk
 | 
				
			||||||
 | 
					DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include ../../../../../rabbitmq-components.mk
 | 
				
			||||||
 | 
					include ../../../../../erlang.mk
 | 
				
			||||||
							
								
								
									
										36
									
								
								deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/src/my_auth_plugin.erl
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										36
									
								
								deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/src/my_auth_plugin.erl
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					%% This Source Code Form is subject to the terms of the Mozilla Public
 | 
				
			||||||
 | 
					%% License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
 | 
					%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%%  Copyright (c) 2025 Broadcom. All Rights Reserved. The term “Broadcom”
 | 
				
			||||||
 | 
					%%  refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-module(my_auth_plugin).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-behaviour(rabbit_authn_backend).
 | 
				
			||||||
 | 
					-behaviour(rabbit_authz_backend).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-export([user_login_authentication/2, user_login_authorization/2,
 | 
				
			||||||
 | 
					         check_vhost_access/3, check_resource_access/4, check_topic_access/4]).
 | 
				
			||||||
 | 
					-export([expiry_timestamp/1]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					%% Implementation of rabbit_authn_backend.
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					user_login_authentication(_, _) ->
 | 
				
			||||||
 | 
					    {error, unknown_user}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					%% Implementation of rabbit_authz_backend.
 | 
				
			||||||
 | 
					%% -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					user_login_authorization(_, _) ->
 | 
				
			||||||
 | 
					    {error, unknown_user}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					check_vhost_access(_AuthUser, _VHostPath, _AuthzData) -> true.
 | 
				
			||||||
 | 
					check_resource_access(_AuthUser, _Resource, _Permission, _Context) -> true.
 | 
				
			||||||
 | 
					check_topic_access(_AuthUser, _Resource, _Permission, _Context) -> true.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expiry_timestamp(_AuthUser) -> never.
 | 
				
			||||||
		Loading…
	
		Reference in New Issue