Skip to content

Commit 8d513c7

Browse files
Propagate all credentials to http backend
except those that starts with rabbit_
1 parent 54ae406 commit 8d513c7

File tree

4 files changed

+64
-16
lines changed

4 files changed

+64
-16
lines changed

deps/rabbitmq_auth_backend_http/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ against the URIs listed in the configuration file. It will add query string
8484
* `username`: the name of the user
8585
* `password`: the password provided (may be missing if e.g. rabbitmq-auth-mechanism-ssl is used)
8686

87+
Note: This plugin may include additional http request parameters in addition to the ones listed above.
88+
For instance, if the user accessed RabbitMQ via the MQTT protocol, it is expected `client_id` and `vhost` request parameters too.
89+
8790
### vhost_path
8891

8992
* `username`: the name of the user

deps/rabbitmq_auth_backend_http/src/rabbit_auth_backend_http.erl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ description() ->
3434

3535
user_login_authentication(Username, AuthProps) ->
3636

37-
case http_req(p(user_path), q([{username, Username}|extractPassword(AuthProps)])) of
37+
case http_req(p(user_path), q([{username, Username}|extractPassword(AuthProps)]++extractOtherCredentials(AuthProps))) of
3838
{error, _} = E -> E;
3939
"deny" -> {refused, "Denied by the backing HTTP service", []};
4040
"allow" ++ Rest -> Tags = [rabbit_data_coercion:to_atom(T) ||
@@ -49,6 +49,7 @@ user_login_authentication(Username, AuthProps) ->
4949
%% Credentials (i.e. password) maybe directly in the password attribute in AuthProps
5050
%% or as a Function with the attribute rabbit_auth_backend_http if the user was already authenticated with http backend
5151
%% or as a Function with the attribute rabbit_auth_backend_cache if the user was already authenticated via cache backend
52+
5253
extractPassword(AuthProps) ->
5354
case proplists:get_value(password, AuthProps, none) of
5455
none ->
@@ -62,6 +63,15 @@ extractPassword(AuthProps) ->
6263
Password -> [{password, Password}]
6364
end.
6465

66+
%% Some protocols may add additional credentials into the AuthProps that should be propagated to
67+
%% the external authentication backends
68+
%% This function excludes any attribute that starts with rabbit_auth_backend_
69+
is_internal_property("rabbit_" ++ _Rest) -> true;
70+
is_internal_property(_Other) -> false.
71+
72+
extractOtherCredentials(AuthProps) ->
73+
[{K,V} || {K,V} <-AuthProps, not is_internal_property(K)].
74+
6575
user_login_authorization(Username, AuthProps) ->
6676
case user_login_authentication(Username, AuthProps) of
6777
{ok, #auth_user{impl = Impl}} -> {ok, Impl};

deps/rabbitmq_auth_backend_http/test/auth_SUITE.erl

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,35 @@
2020
{vhost_path, "http://localhost:" ++ integer_to_list(?AUTH_PORT) ++ "/auth/vhost"},
2121
{resource_path, "http://localhost:" ++ integer_to_list(?AUTH_PORT) ++ "/auth/resource"},
2222
{topic_path, "http://localhost:" ++ integer_to_list(?AUTH_PORT) ++ "/auth/topic"}]).
23-
-define(ALLOWED_USER, #{username => <<"Ala">>,
23+
-define(ALLOWED_USER, #{username => <<"Ala1">>,
2424
password => <<"Kocur">>,
25+
expected_credentials => [username, password],
2526
tags => [policymaker, monitoring]}).
26-
-define(DENIED_USER, #{username => <<"Alice">>, password => <<"Cat">>}).
27+
-define(ALLOWED_USER_WITH_EXTRA_CREDENTIALS, #{username => <<"Ala2">>,
28+
password => <<"Kocur">>,
29+
client_id => <<"some_id">>,
30+
expected_credentials => [username, password, client_id],
31+
tags => [policymaker, monitoring]}).
32+
-define(DENIED_USER, #{username => <<"Alice">>,
33+
password => <<"Cat">>
34+
}).
2735

28-
all() -> [grants_access_to_user, denies_access_to_user].
36+
all() -> [grants_access_to_user,
37+
denies_access_to_user,
38+
grants_access_to_user_passing_additional_required_authprops,
39+
grants_access_to_user_skipping_internal_authprops].
2940

3041
init_per_suite(Config) ->
3142
configure_http_auth_backend(),
32-
#{username := Username, password := Password, tags := Tags} = ?ALLOWED_USER,
33-
start_http_auth_server(?AUTH_PORT, ?USER_PATH, #{Username => {Password, Tags}}),
34-
[{allowed_user, ?ALLOWED_USER}, {denied_user, ?DENIED_USER} | Config].
43+
{User1, Tuple1} = extractUserTuple(?ALLOWED_USER),
44+
{User2, Tuple2} = extractUserTuple(?ALLOWED_USER_WITH_EXTRA_CREDENTIALS),
45+
start_http_auth_server(?AUTH_PORT, ?USER_PATH, #{User1 => Tuple1, User2 => Tuple2}),
46+
[{allowed_user, ?ALLOWED_USER},
47+
{allowed_user_with_extra_credentials, ?ALLOWED_USER_WITH_EXTRA_CREDENTIALS},
48+
{denied_user, ?DENIED_USER} | Config].
49+
extractUserTuple(User) ->
50+
#{username := Username, password := Password, tags := Tags, expected_credentials := ExpectedCredentials} = User,
51+
{Username, {Password, Tags, ExpectedCredentials}}.
3552

3653
end_per_suite(_Config) ->
3754
stop_http_auth_server().
@@ -47,6 +64,20 @@ denies_access_to_user(Config) ->
4764
?assertMatch({refused, "Denied by the backing HTTP service", []},
4865
rabbit_auth_backend_http:user_login_authentication(U, [{password, P}])).
4966

67+
68+
grants_access_to_user_passing_additional_required_authprops(Config) ->
69+
#{username := U, password := P, tags := T, client_id := ClientId} = ?config(allowed_user_with_extra_credentials, Config),
70+
{ok, User} = rabbit_auth_backend_http:user_login_authentication(U, [{password, P}, {client_id, ClientId}]),
71+
?assertMatch({U, T, P},
72+
{User#auth_user.username, User#auth_user.tags, (User#auth_user.impl)()}).
73+
74+
grants_access_to_user_skipping_internal_authprops(Config) ->
75+
#{username := U, password := P, tags := T, client_id := ClientId} = ?config(allowed_user_with_extra_credentials, Config),
76+
{ok, User} = rabbit_auth_backend_http:user_login_authentication(U,
77+
[{password, P}, {client_id, ClientId}, {rabbit_any_internal_property, <<"some value">>}]),
78+
?assertMatch({U, T, P},
79+
{User#auth_user.username, User#auth_user.tags, (User#auth_user.impl)()}).
80+
5081
%%% HELPERS
5182

5283
configure_http_auth_backend() ->

deps/rabbitmq_auth_backend_http/test/auth_http_mock.erl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@
66

77
init(Req = #{method := <<"GET">>}, Users) ->
88
QsVals = cowboy_req:parse_qs(Req),
9-
Reply = authenticate(proplists:get_value(<<"username">>, QsVals),
10-
proplists:get_value(<<"password">>, QsVals),
11-
Users),
9+
Reply = authenticate(QsVals, Users),
1210
Req2 = cowboy_req:reply(200, #{<<"content-type">> => <<"text/plain">>}, Reply, Req),
1311
{ok, Req2, Users}.
1412

1513
%%% HELPERS
1614

17-
authenticate(Username, Password, Users) ->
15+
authenticate(QsVals, Users) ->
16+
Username = proplists:get_value(<<"username">>, QsVals),
17+
Password = proplists:get_value(<<"password">>, QsVals),
1818
case maps:get(Username, Users, undefined) of
19-
{MatchingPassword, Tags} when Password =:= MatchingPassword ->
20-
StringTags = lists:map(fun(T) -> io_lib:format("~ts", [T]) end, Tags),
21-
<<"allow ", (list_to_binary(string:join(StringTags, " ")))/binary>>;
22-
{_OtherPassword, _} ->
19+
{MatchingPassword, Tags, ExpectedCredentials} when Password =:= MatchingPassword ->
20+
case lists:all(fun(C) -> proplists:is_defined(list_to_binary(rabbit_data_coercion:to_list(C)),QsVals) end, ExpectedCredentials) of
21+
true -> StringTags = lists:map(fun(T) -> io_lib:format("~ts", [T]) end, Tags),
22+
<<"allow ", (list_to_binary(string:join(StringTags, " ")))/binary>>;
23+
false -> ct:log("Missing required attributes. Expected ~p, Found: ~p", [ExpectedCredentials, QsVals]),
24+
<<"deny">>
25+
end;
26+
{_OtherPassword, _, _} ->
2327
<<"deny">>;
2428
undefined ->
2529
<<"deny">>
26-
end.
30+
end.

0 commit comments

Comments
 (0)