diff --git a/deps/rabbit/src/rabbit.erl b/deps/rabbit/src/rabbit.erl index 6224c70127..2bca266ca7 100644 --- a/deps/rabbit/src/rabbit.erl +++ b/deps/rabbit/src/rabbit.erl @@ -52,6 +52,14 @@ {requires, pre_boot}, {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_boot_step({rabbit_alarm, [{description, "alarm handler"}, diff --git a/deps/rabbit/src/rabbit_access_control.erl b/deps/rabbit/src/rabbit_access_control.erl index 3fb09726b2..b0b2ecf0f8 100644 --- a/deps/rabbit/src/rabbit_access_control.erl +++ b/deps/rabbit/src/rabbit_access_control.erl @@ -10,6 +10,7 @@ -include_lib("rabbit_common/include/rabbit.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, check_vhost_access/4, check_resource_access/4, check_topic_access/4, 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 + %% " 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 (rabbit_types:username(), rabbit_types:password()) -> {'ok', rabbit_types:user()} | diff --git a/deps/rabbit/src/rabbit_plugins.erl b/deps/rabbit/src/rabbit_plugins.erl index d4d0ccd4c5..067c6e54db 100644 --- a/deps/rabbit/src/rabbit_plugins.erl +++ b/deps/rabbit/src/rabbit_plugins.erl @@ -8,12 +8,13 @@ -module(rabbit_plugins). -include_lib("rabbit_common/include/rabbit.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([validate_plugins/1, format_invalid_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([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([is_version_supported/2, validate_plugins/2]). @@ -130,7 +131,7 @@ setup() -> -spec active() -> [plugin_name()]. active() -> - InstalledPlugins = plugin_names(list(plugins_dir())), + InstalledPlugins = plugin_names(list()), [App || {App, _, _} <- rabbit_misc:which_applications(), lists:member(App, InstalledPlugins)]. @@ -157,6 +158,13 @@ is_enabled_on_node(Name, Node) -> _Class:_Reason:_Stacktrace -> false 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. -spec list(string()) -> [#plugin{}]. @@ -228,7 +236,7 @@ strictly_plugins(Plugins, AllPlugins) -> -spec strictly_plugins([plugin_name()]) -> [plugin_name()]. strictly_plugins(Plugins) -> - AllPlugins = list(plugins_dir()), + AllPlugins = list(), lists:filter( fun(Name) -> is_strictly_plugin(lists:keyfind(Name, #plugin.name, AllPlugins)) @@ -279,11 +287,61 @@ running_plugins() -> ActivePlugins = active(), {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) -> ExpandDir = plugins_expand_dir(), - AllPlugins = list(plugins_dir()), + AllPlugins = list(), Wanted = dependencies(false, Enabled, AllPlugins), WantedPlugins = lookup_plugins(Wanted, AllPlugins), {ValidPlugins, Problems} = validate_plugins(WantedPlugins), diff --git a/deps/rabbit/test/rabbit_access_control_SUITE.erl b/deps/rabbit/test/rabbit_access_control_SUITE.erl index a2e4660ffa..2911769830 100644 --- a/deps/rabbit/test/rabbit_access_control_SUITE.erl +++ b/deps/rabbit/test/rabbit_access_control_SUITE.erl @@ -2,34 +2,54 @@ %% 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) 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. %% -module(rabbit_access_control_SUITE). --compile(export_all). - -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"). +-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 %%%=================================================================== all() -> - [{group, tests}]. - -%% replicate eunit like test resolution -all_tests() -> - [F - || {F, _} <- ?MODULE:module_info(functions), - re:run(atom_to_list(F), "_test$") /= nomatch]. + [{group, unit_tests}, + {group, integration_tests}]. 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) -> Config. @@ -37,20 +57,74 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(integration_tests, Config) -> + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config); init_per_group(_Group, Config) -> Config. +end_per_group(integration_tests, Config) -> + rabbit_ct_helpers:run_teardown_steps(Config); end_per_group(_Group, _Config) -> 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) -> 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) -> meck:unload(), ok. -expiry_timestamp_test(_) -> +%% ------------------------------------------------------------------- +%% Testcases. +%% ------------------------------------------------------------------- + +expiry_timestamp(_) -> %% test rabbit_access_control:expiry_timestamp/1 returns the earliest expiry time Now = os:system_time(seconds), BeforeNow = Now - 60, @@ -102,3 +176,265 @@ expiry_timestamp_test(_) -> {rabbit_expiry_backend, unused}]}, ?assertEqual(Now, rabbit_access_control:expiry_timestamp(User7)), 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. diff --git a/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/Makefile b/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/Makefile new file mode 100644 index 0000000000..acfa83e494 --- /dev/null +++ b/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/Makefile @@ -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 diff --git a/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/src/my_auth_plugin.erl b/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/src/my_auth_plugin.erl new file mode 100644 index 0000000000..2c663badf3 --- /dev/null +++ b/deps/rabbit/test/rabbit_access_control_SUITE_data/my_auth_plugin/src/my_auth_plugin.erl @@ -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.