rabbitmq-server/deps/rabbit/test/unit_access_control_SUITE.erl

319 lines
12 KiB
Erlang

% 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) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
%%
-module(unit_access_control_SUITE).
-include_lib("common_test/include/ct.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
all() ->
[
{group, sequential_tests},
{group, parallel_tests}
].
groups() ->
[
{parallel_tests, [parallel], [
password_hashing,
version_negotiation
]},
{sequential_tests, [], [
login_with_credentials_but_no_password,
login_of_passwordless_user,
set_tags_for_passwordless_user,
change_password,
auth_backend_internal_expand_topic_permission,
rabbit_direct_extract_extra_auth_props
]}
].
%% -------------------------------------------------------------------
%% Testsuite setup/teardown
%% -------------------------------------------------------------------
init_per_suite(Config) ->
rabbit_ct_helpers:log_environment(),
rabbit_ct_helpers:run_setup_steps(Config).
end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config).
init_per_group(Group, Config) ->
Config1 = rabbit_ct_helpers:set_config(Config, [
{rmq_nodename_suffix, Group},
{rmq_nodes_count, 1}
]),
rabbit_ct_helpers:run_steps(Config1,
rabbit_ct_broker_helpers:setup_steps() ++
rabbit_ct_client_helpers:setup_steps()).
end_per_group(_Group, Config) ->
rabbit_ct_helpers:run_steps(Config,
rabbit_ct_client_helpers:teardown_steps() ++
rabbit_ct_broker_helpers:teardown_steps()).
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
end_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_finished(Config, Testcase).
%% ---------------------------------------------------------------------------
%% Test Cases
%% ---------------------------------------------------------------------------
password_hashing(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
?MODULE, password_hashing1, [Config]).
password_hashing1(_Config) ->
rabbit_password_hashing_sha256 = rabbit_password:hashing_mod(),
application:set_env(rabbit, password_hashing_module,
rabbit_password_hashing_md5),
rabbit_password_hashing_md5 = rabbit_password:hashing_mod(),
application:set_env(rabbit, password_hashing_module,
rabbit_password_hashing_sha256),
rabbit_password_hashing_sha256 = rabbit_password:hashing_mod(),
rabbit_password_hashing_sha256 =
rabbit_password:hashing_mod(rabbit_password_hashing_sha256),
rabbit_password_hashing_md5 =
rabbit_password:hashing_mod(rabbit_password_hashing_md5),
rabbit_password_hashing_md5 =
rabbit_password:hashing_mod(undefined),
rabbit_password_hashing_md5 =
rabbit_auth_backend_internal:hashing_module_for_user(
internal_user:new()),
rabbit_password_hashing_md5 =
rabbit_auth_backend_internal:hashing_module_for_user(
internal_user:new({hashing_algorithm, undefined})),
rabbit_password_hashing_md5 =
rabbit_auth_backend_internal:hashing_module_for_user(
internal_user:new({hashing_algorithm, rabbit_password_hashing_md5})),
rabbit_password_hashing_sha256 =
rabbit_auth_backend_internal:hashing_module_for_user(
internal_user:new({hashing_algorithm, rabbit_password_hashing_sha256})),
passed.
change_password(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
?MODULE, change_password1, [Config]).
change_password1(_Config) ->
UserName = <<"test_user">>,
Password = <<"test_password">>,
case rabbit_auth_backend_internal:lookup_user(UserName) of
{ok, _} -> rabbit_auth_backend_internal:delete_user(UserName, <<"acting-user">>);
_ -> ok
end,
ok = application:set_env(rabbit, password_hashing_module,
rabbit_password_hashing_md5),
ok = rabbit_auth_backend_internal:add_user(UserName, Password, <<"acting-user">>),
{ok, #auth_user{username = UserName}} =
rabbit_auth_backend_internal:user_login_authentication(
UserName, [{password, Password}]),
ok = application:set_env(rabbit, password_hashing_module,
rabbit_password_hashing_sha256),
{ok, #auth_user{username = UserName}} =
rabbit_auth_backend_internal:user_login_authentication(
UserName, [{password, Password}]),
NewPassword = <<"test_password1">>,
ok = rabbit_auth_backend_internal:change_password(UserName, NewPassword,
<<"acting-user">>),
{ok, #auth_user{username = UserName}} =
rabbit_auth_backend_internal:user_login_authentication(
UserName, [{password, NewPassword}]),
{refused, _, [UserName]} =
rabbit_auth_backend_internal:user_login_authentication(
UserName, [{password, Password}]),
passed.
login_with_credentials_but_no_password(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
?MODULE, login_with_credentials_but_no_password1, [Config]).
login_with_credentials_but_no_password1(_Config) ->
Username = <<"login_with_credentials_but_no_password-user">>,
Password = <<"login_with_credentials_but_no_password-password">>,
ok = rabbit_auth_backend_internal:add_user(Username, Password, <<"acting-user">>),
?assertMatch(
{refused, _Message, [Username]},
rabbit_auth_backend_internal:user_login_authentication(Username,
[{key, <<"value">>}])),
ok = rabbit_auth_backend_internal:delete_user(Username, <<"acting-user">>),
passed.
%% passwordless users are not supposed to be used with
%% this backend (and PLAIN authentication mechanism in general)
login_of_passwordless_user(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
?MODULE, login_of_passwordless_user1, [Config]).
login_of_passwordless_user1(_Config) ->
Username = <<"login_of_passwordless_user-user">>,
Password = <<"">>,
ok = rabbit_auth_backend_internal:add_user(Username, Password, <<"acting-user">>),
?assertMatch(
{refused, _Message, [Username]},
rabbit_auth_backend_internal:user_login_authentication(Username,
[{password, <<"">>}])),
?assertMatch(
{refused, _Format, [Username]},
rabbit_auth_backend_internal:user_login_authentication(Username,
[{password, ""}])),
ok = rabbit_auth_backend_internal:delete_user(Username, <<"acting-user">>),
passed.
set_tags_for_passwordless_user(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
?MODULE, set_tags_for_passwordless_user1, [Config]).
set_tags_for_passwordless_user1(_Config) ->
Username = <<"set_tags_for_passwordless_user">>,
Password = <<"set_tags_for_passwordless_user">>,
ok = rabbit_auth_backend_internal:add_user(Username, Password,
<<"acting-user">>),
ok = rabbit_auth_backend_internal:clear_password(Username,
<<"acting-user">>),
ok = rabbit_auth_backend_internal:set_tags(Username, [management],
<<"acting-user">>),
{ok, User1} = rabbit_auth_backend_internal:lookup_user(Username),
?assertEqual([management], internal_user:get_tags(User1)),
ok = rabbit_auth_backend_internal:set_tags(Username, [management, policymaker],
<<"acting-user">>),
{ok, User2} = rabbit_auth_backend_internal:lookup_user(Username),
?assertEqual([management, policymaker], internal_user:get_tags(User2)),
ok = rabbit_auth_backend_internal:set_tags(Username, [],
<<"acting-user">>),
{ok, User3} = rabbit_auth_backend_internal:lookup_user(Username),
?assertEqual([], internal_user:get_tags(User3)),
ok = rabbit_auth_backend_internal:delete_user(Username,
<<"acting-user">>),
passed.
rabbit_direct_extract_extra_auth_props(_Config) ->
{ok, CSC} = code_server_cache:start_link(),
% no protocol to extract
[] = rabbit_direct:extract_extra_auth_props(
{<<"guest">>, <<"guest">>}, <<"/">>, 1,
[{name,<<"127.0.0.1:52366 -> 127.0.0.1:1883">>}]),
% protocol to extract, but no module to call
[] = rabbit_direct:extract_extra_auth_props(
{<<"guest">>, <<"guest">>}, <<"/">>, 1,
[{protocol, {'PROTOCOL_WITHOUT_MODULE', "1.0"}}]),
% see rabbit_dummy_protocol_connection_info module
% protocol to extract, module that returns a client ID
[{client_id, <<"DummyClientId">>}] = rabbit_direct:extract_extra_auth_props(
{<<"guest">>, <<"guest">>}, <<"/">>, 1,
[{protocol, {'DUMMY_PROTOCOL', "1.0"}}]),
% protocol to extract, but error thrown in module
[] = rabbit_direct:extract_extra_auth_props(
{<<"guest">>, <<"guest">>}, <<"/">>, -1,
[{protocol, {'DUMMY_PROTOCOL', "1.0"}}]),
gen_server:stop(CSC),
ok.
auth_backend_internal_expand_topic_permission(_Config) ->
ExpandMap = #{<<"username">> => <<"guest">>, <<"vhost">> => <<"default">>},
%% simple case
<<"services/default/accounts/guest/notifications">> =
rabbit_auth_backend_internal:expand_topic_permission(
<<"services/{vhost}/accounts/{username}/notifications">>,
ExpandMap
),
%% replace variable twice
<<"services/default/accounts/default/guest/notifications">> =
rabbit_auth_backend_internal:expand_topic_permission(
<<"services/{vhost}/accounts/{vhost}/{username}/notifications">>,
ExpandMap
),
%% nothing to replace
<<"services/accounts/notifications">> =
rabbit_auth_backend_internal:expand_topic_permission(
<<"services/accounts/notifications">>,
ExpandMap
),
%% the expand map isn't defined
<<"services/{vhost}/accounts/{username}/notifications">> =
rabbit_auth_backend_internal:expand_topic_permission(
<<"services/{vhost}/accounts/{username}/notifications">>,
undefined
),
%% the expand map is empty
<<"services/{vhost}/accounts/{username}/notifications">> =
rabbit_auth_backend_internal:expand_topic_permission(
<<"services/{vhost}/accounts/{username}/notifications">>,
#{}
),
ok.
%% Test AMQP 1.0 §2.2
version_negotiation(Config) ->
ok = rabbit_ct_broker_helpers:rpc(Config, ?MODULE, version_negotiation1, [Config]).
version_negotiation1(Config) ->
Hostname = ?config(rmq_hostname, Config),
Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp),
[?assertEqual(<<"AMQP",3,1,0,0>>,
version_negotiation2(Hostname, Port, Vsn)) ||
Vsn <- [<<"AMQP",0,1,0,0>>,
<<"AMQP",0,1,0,1>>,
<<"AMQP",0,1,1,0>>,
<<"AMQP",0,9,1,0>>,
<<"AMQP",0,0,8,0>>,
<<"AMQP",1,1,0,0>>,
<<"AMQP",2,1,0,0>>,
<<"AMQP",3,1,0,0>>,
<<"AMQP",3,1,0,1>>,
<<"AMQP",3,1,0,1>>,
<<"AMQP",4,1,0,0>>,
<<"AMQP",9,1,0,0>>,
<<"XXXX",0,1,0,0>>,
<<"XXXX",0,0,9,1>>
]],
[?assertEqual(<<"AMQP",0,0,9,1>>,
version_negotiation2(Hostname, Port, Vsn)) ||
Vsn <- [<<"AMQP",0,0,9,2>>,
<<"AMQP",0,0,10,0>>,
<<"AMQP",0,0,10,1>>]],
ok.
version_negotiation2(Hostname, Port, Header) ->
{ok, C} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
ok = gen_tcp:send(C, Header),
{ok, ServerVersion} = gen_tcp:recv(C, 8, 100),
ok = gen_tcp:close(C),
ServerVersion.