Skip to content

Commit 1da0f9d

Browse files
Merge pull request #13466 from rabbitmq/mergify/bp/v4.1.x/pr-13465
By @aaron-seo: Optionally allow separate configuration of auth_backends for all HTTP-based plugins (protocols, APIs) (backport #13465)
2 parents 561b410 + 888b57c commit 1da0f9d

File tree

5 files changed

+239
-2
lines changed

5 files changed

+239
-2
lines changed

deps/rabbit/src/rabbit_access_control.erl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
-include_lib("rabbit_common/include/rabbit.hrl").
1111

12-
-export([check_user_pass_login/2, check_user_login/2, check_user_loopback/2,
12+
-export([check_user_pass_login/2, check_user_login/2, check_user_login/3, check_user_loopback/2,
1313
check_vhost_access/4, check_resource_access/4, check_topic_access/4,
1414
check_user_id/2]).
1515

@@ -33,6 +33,14 @@ check_user_pass_login(Username, Password) ->
3333
check_user_login(Username, AuthProps) ->
3434
%% extra auth properties like MQTT client id are in AuthProps
3535
{ok, Modules} = application:get_env(rabbit, auth_backends),
36+
check_user_login(Username, AuthProps, Modules).
37+
38+
-spec check_user_login
39+
(rabbit_types:username(), [{atom(), any()}], term()) ->
40+
{'ok', rabbit_types:user()} |
41+
{'refused', rabbit_types:username(), string(), [any()]}.
42+
43+
check_user_login(Username, AuthProps, Modules) ->
3644
try
3745
lists:foldl(
3846
fun (rabbit_auth_backend_cache=ModN, {refused, _, _, _}) ->
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
% vim:ft=erlang:
2+
%% ----------------------------------------------------------------------------
3+
%% RabbitMQ Web Dispatch
4+
%%
5+
%% ----------------------------------------------------------------------------
6+
7+
%% ===========================================================================
8+
%% Auth Backends
9+
10+
%% Select an authentication backend to use for the management plugin. RabbitMQ provides an
11+
%% internal backend in the core.
12+
%%
13+
%% {http_dispatch.auth_backends, [rabbit_auth_backend_internal]},
14+
15+
{translation, "rabbitmq_web_dispatch.auth_backends",
16+
fun(Conf) ->
17+
Settings = cuttlefish_variable:filter_by_prefix("http_dispatch.auth_backends", Conf),
18+
BackendModule = fun
19+
(internal) -> rabbit_auth_backend_internal;
20+
(ldap) -> rabbit_auth_backend_ldap;
21+
(http) -> rabbit_auth_backend_http;
22+
(oauth) -> rabbit_auth_backend_oauth2;
23+
(oauth2) -> rabbit_auth_backend_oauth2;
24+
(cache) -> rabbit_auth_backend_cache;
25+
(amqp) -> rabbit_auth_backend_amqp;
26+
(dummy) -> rabbit_auth_backend_dummy;
27+
(Other) when is_atom(Other) -> Other;
28+
(_) -> cuttlefish:invalid("Unknown/unsupported auth backend")
29+
end,
30+
AuthBackends = [{Num, {default, BackendModule(V)}} || {["http_dispatch", "auth_backends", Num], V} <- Settings],
31+
AuthNBackends = [{Num, {authn, BackendModule(V)}} || {["http_dispatch", "auth_backends", Num, "authn"], V} <- Settings],
32+
AuthZBackends = [{Num, {authz, BackendModule(V)}} || {["http_dispatch", "auth_backends", Num, "authz"], V} <- Settings],
33+
Backends = lists:foldl(
34+
fun({NumStr, {Type, V}}, Acc) ->
35+
Num = case catch list_to_integer(NumStr) of
36+
N when is_integer(N) -> N;
37+
Err ->
38+
cuttlefish:invalid(
39+
iolist_to_binary(io_lib:format(
40+
"Auth backend position in the chain should be an integer ~p", [Err])))
41+
end,
42+
NewVal = case dict:find(Num, Acc) of
43+
{ok, {AuthN, AuthZ}} ->
44+
case {Type, AuthN, AuthZ} of
45+
{authn, undefined, _} ->
46+
{V, AuthZ};
47+
{authz, _, undefined} ->
48+
{AuthN, V};
49+
_ ->
50+
cuttlefish:invalid(
51+
iolist_to_binary(
52+
io_lib:format(
53+
"Auth backend already defined for the ~pth ~p backend",
54+
[Num, Type])))
55+
end;
56+
error ->
57+
case Type of
58+
authn -> {V, undefined};
59+
authz -> {undefined, V};
60+
default -> {V, V}
61+
end
62+
end,
63+
dict:store(Num, NewVal, Acc)
64+
end,
65+
dict:new(),
66+
AuthBackends ++ AuthNBackends ++ AuthZBackends),
67+
lists:map(
68+
fun
69+
({Num, {undefined, AuthZ}}) ->
70+
cuttlefish:warn(
71+
io_lib:format(
72+
"Auth backend undefined for the ~pth authz backend. Using ~p",
73+
[Num, AuthZ])),
74+
{AuthZ, AuthZ};
75+
({Num, {AuthN, undefined}}) ->
76+
cuttlefish:warn(
77+
io_lib:format(
78+
"Authz backend undefined for the ~pth authn backend. Using ~p",
79+
[Num, AuthN])),
80+
{AuthN, AuthN};
81+
({_Num, {Auth, Auth}}) -> Auth;
82+
({_Num, {AuthN, AuthZ}}) -> {AuthN, AuthZ}
83+
end,
84+
lists:keysort(1, dict:to_list(Backends)))
85+
end}.
86+
87+
{mapping, "http_dispatch.auth_backends.$num", "rabbitmq_web_dispatch.auth_backends", [
88+
{datatype, atom}
89+
]}.
90+
91+
{mapping, "http_dispatch.auth_backends.$num.authn", "rabbitmq_web_dispatch.auth_backends",[
92+
{datatype, atom}
93+
]}.
94+
95+
{mapping, "http_dispatch.auth_backends.$num.authz", "rabbitmq_web_dispatch.auth_backends",[
96+
{datatype, atom}
97+
]}.
98+
99+
%{mapping, "management.test_config", "rabbitmq_management.test_config",
100+
% [{datatype, {enum, [true, false]}}]}.

deps/rabbitmq_web_dispatch/src/rabbit_web_dispatch_access_control.erl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ is_authorized(ReqData, Context, Username, Password, ErrorMsg, Fun, AuthConfig, R
141141
_ -> []
142142
end,
143143
{IP, _} = cowboy_req:peer(ReqData),
144-
case rabbit_access_control:check_user_login(Username, AuthProps) of
144+
145+
{ok, AuthBackends} = get_auth_backends(),
146+
147+
case rabbit_access_control:check_user_login(Username, AuthProps, AuthBackends) of
145148
{ok, User = #user{username = ResolvedUsername, tags = Tags}} ->
146149
case rabbit_access_control:check_user_loopback(ResolvedUsername, IP) of
147150
ok ->
@@ -359,3 +362,11 @@ log_access_control_result(NotOK) ->
359362

360363
is_basic_auth_disabled(#auth_settings{basic_auth_enabled = Enabled}) ->
361364
not Enabled.
365+
366+
get_auth_backends() ->
367+
case application:get_env(rabbitmq_web_dispatch, auth_backends) of
368+
{ok, Backends} ->
369+
{ok, Backends};
370+
undefined ->
371+
application:get_env(rabbit, auth_backends)
372+
end.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
8+
-module(config_schema_SUITE).
9+
10+
-compile(export_all).
11+
12+
all() ->
13+
[
14+
run_snippets
15+
].
16+
17+
%% -------------------------------------------------------------------
18+
%% Testsuite setup/teardown.
19+
%% -------------------------------------------------------------------
20+
21+
init_per_suite(Config) ->
22+
rabbit_ct_helpers:log_environment(),
23+
Config1 = rabbit_ct_helpers:run_setup_steps(Config),
24+
rabbit_ct_config_schema:init_schemas(rabbitmq_web_dispatch, Config1).
25+
26+
27+
end_per_suite(Config) ->
28+
rabbit_ct_helpers:run_teardown_steps(Config).
29+
30+
init_per_testcase(Testcase, Config) ->
31+
rabbit_ct_helpers:testcase_started(Config, Testcase),
32+
Config1 = rabbit_ct_helpers:set_config(Config, [
33+
{rmq_nodename_suffix, Testcase}
34+
]),
35+
rabbit_ct_helpers:run_steps(Config1,
36+
rabbit_ct_broker_helpers:setup_steps() ++
37+
rabbit_ct_client_helpers:setup_steps()).
38+
39+
end_per_testcase(Testcase, Config) ->
40+
Config1 = rabbit_ct_helpers:run_steps(Config,
41+
rabbit_ct_client_helpers:teardown_steps() ++
42+
rabbit_ct_broker_helpers:teardown_steps()),
43+
rabbit_ct_helpers:testcase_finished(Config1, Testcase).
44+
45+
%% -------------------------------------------------------------------
46+
%% Testcases.
47+
%% -------------------------------------------------------------------
48+
49+
run_snippets(Config) ->
50+
ok = rabbit_ct_broker_helpers:rpc(Config, 0,
51+
?MODULE, run_snippets1, [Config]).
52+
53+
run_snippets1(Config) ->
54+
rabbit_ct_config_schema:run_snippets(Config).
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
% vim:ft=erlang:
2+
%
3+
4+
[{internal_auth_backend,
5+
"http_dispatch.auth_backends.1 = internal",
6+
[{rabbitmq_web_dispatch,[{auth_backends,[rabbit_auth_backend_internal]}]}],
7+
[]},
8+
{ldap_auth_backend,
9+
"http_dispatch.auth_backends.1 = ldap",
10+
[{rabbitmq_web_dispatch,[{auth_backends,[rabbit_auth_backend_ldap]}]}],
11+
[]},
12+
{http_auth_backend,
13+
"http_dispatch.auth_backends.1 = http",
14+
[{rabbitmq_web_dispatch,[{auth_backends,[rabbit_auth_backend_http]}]}],
15+
[]},
16+
{oauth2_auth_backend,
17+
"http_dispatch.auth_backends.1 = oauth2",
18+
[{rabbitmq_web_dispatch,[{auth_backends,[rabbit_auth_backend_oauth2]}]}],
19+
[]},
20+
{multiple_auth_backends,
21+
"http_dispatch.auth_backends.1 = ldap
22+
http_dispatch.auth_backends.2 = internal",
23+
[{rabbitmq_web_dispatch,
24+
[{auth_backends,
25+
[rabbit_auth_backend_ldap,rabbit_auth_backend_internal]}]}],
26+
[]},
27+
{full_name_auth_backend,
28+
"http_dispatch.auth_backends.1 = ldap
29+
# uses module name instead of a short alias, \"http\"
30+
http_dispatch.auth_backends.2 = rabbit_auth_backend_http",
31+
[{rabbitmq_web_dispatch,
32+
[{auth_backends,[rabbit_auth_backend_ldap,rabbit_auth_backend_http]}]}],
33+
[]},
34+
{third_party_auth_backend,
35+
"http_dispatch.auth_backends.1.authn = internal
36+
# uses module name because this backend is from a 3rd party
37+
http_dispatch.auth_backends.1.authz = rabbit_auth_backend_ip_range",
38+
[{rabbitmq_web_dispatch,
39+
[{auth_backends,
40+
[{rabbit_auth_backend_internal,rabbit_auth_backend_ip_range}]}]}],
41+
[]},
42+
{authn_authz_backend,
43+
"http_dispatch.auth_backends.1.authn = ldap
44+
http_dispatch.auth_backends.1.authz = internal",
45+
[{rabbitmq_web_dispatch,
46+
[{auth_backends,
47+
[{rabbit_auth_backend_ldap,rabbit_auth_backend_internal}]}]}],
48+
[]},
49+
{authn_authz_multiple_backends,
50+
"http_dispatch.auth_backends.1.authn = ldap
51+
http_dispatch.auth_backends.1.authz = internal
52+
http_dispatch.auth_backends.2 = internal",
53+
[{rabbitmq_web_dispatch,
54+
[{auth_backends,
55+
[{rabbit_auth_backend_ldap,rabbit_auth_backend_internal},
56+
rabbit_auth_backend_internal]}]}],
57+
[]},
58+
{authn_backend_only,
59+
"http_dispatch.auth_backends.1.authn = ldap",
60+
[{rabbitmq_web_dispatch,
61+
[{auth_backends,
62+
[{rabbit_auth_backend_ldap,rabbit_auth_backend_ldap}]}]}],
63+
[]}
64+
].

0 commit comments

Comments
 (0)