Merge pull request #14651 from rabbitmq/revert-14414-lukebakken/ldap-validation-api
Trigger a 4.2.x alpha release build / trigger_alpha_build (push) Waiting to run Details
Test (make) / Build and Xref (1.18, 26) (push) Waiting to run Details
Test (make) / Build and Xref (1.18, 27) (push) Waiting to run Details
Test (make) / Build and Xref (1.18, 28) (push) Waiting to run Details
Test (make) / Test (1.18, 28, khepri) (push) Waiting to run Details
Test (make) / Test (1.18, 28, mnesia) (push) Waiting to run Details
Test (make) / Test mixed clusters (1.18, 28, khepri) (push) Waiting to run Details
Test (make) / Test mixed clusters (1.18, 28, mnesia) (push) Waiting to run Details
Test (make) / Type check (1.18, 28) (push) Waiting to run Details
Test Authentication/Authorization backends via mutiple messaging protocols / selenium (chrome, 1.17.3, 27.3) (push) Has been cancelled Details
Test Management UI with Selenium / selenium (chrome, 1.17.3, 27.3) (push) Has been cancelled Details
Test Authentication/Authorization backends via mutiple messaging protocols / summary-selenium (push) Has been cancelled Details

Revert "Implement LDAP credentials validation via HTTP API"
This commit is contained in:
Michael Klishin 2025-09-30 13:10:29 -04:00 committed by GitHub
commit a95c3192fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 14 additions and 747 deletions

View File

@ -35,7 +35,7 @@ define PROJECT_APP_EXTRA_KEYS
endef
LOCAL_DEPS = eldap public_key
DEPS = rabbit_common rabbit rabbitmq_management
DEPS = rabbit_common rabbit
TEST_DEPS = ct_helper rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client
dep_ct_helper = git https://github.com/extend/ct_helper.git master

View File

@ -1,267 +0,0 @@
%% 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(rabbit_auth_backend_ldap_mgmt).
-behaviour(rabbit_mgmt_extension).
-export([dispatcher/0, web_ui/0]).
-export([init/2,
content_types_accepted/2,
allowed_methods/2,
resource_exists/2,
is_authorized/2,
accept_content/2]).
-include_lib("kernel/include/logger.hrl").
-include_lib("rabbitmq_web_dispatch/include/rabbitmq_web_dispatch_records.hrl").
dispatcher() -> [{"/ldap/validate/simple-bind", ?MODULE, []}].
web_ui() -> [].
%%--------------------------------------------------------------------
init(Req, _Opts) ->
{cowboy_rest, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
content_types_accepted(ReqData, Context) ->
{[{'*', accept_content}], ReqData, Context}.
allowed_methods(ReqData, Context) ->
{[<<"PUT">>, <<"OPTIONS">>], ReqData, Context}.
resource_exists(ReqData, Context) ->
{true, ReqData, Context}.
is_authorized(ReqData, Context) ->
rabbit_mgmt_util:is_authorized(ReqData, Context).
accept_content(ReqData0, Context) ->
F = fun (_Values, BodyMap, ReqData1) ->
try
Port = safe_parse_int(maps:get(port, BodyMap, 389), "port"),
UseSsl = safe_parse_bool(maps:get(use_ssl, BodyMap, false), "use_ssl"),
UseStartTls = safe_parse_bool(maps:get(use_starttls, BodyMap, false), "use_starttls"),
Servers = maps:get(servers, BodyMap, []),
UserDN = maps:get(user_dn, BodyMap, <<"">>),
Password = maps:get(password, BodyMap, <<"">>),
Options0 = [
{port, Port},
{timeout, 5000}
],
{ok, Options1} = maybe_add_ssl_options(Options0, UseSsl, BodyMap),
case eldap:open(Servers, Options1) of
{ok, LDAP} ->
Result = case maybe_starttls(LDAP, UseStartTls, BodyMap) of
ok ->
case eldap:simple_bind(LDAP, UserDN, Password) of
ok ->
{true, ReqData1, Context};
{error, invalidCredentials} ->
rabbit_mgmt_util:unprocessable_entity("invalid LDAP credentials: "
"authentication failure",
ReqData1, Context);
{error, unwillingToPerform} ->
rabbit_mgmt_util:unprocessable_entity("invalid LDAP credentials: "
"authentication failure",
ReqData1, Context);
{error, invalidDNSyntax} ->
rabbit_mgmt_util:unprocessable_entity("invalid LDAP credentials: "
"DN syntax invalid / too long",
ReqData1, Context);
{error, E} ->
Reason = unicode_format(E),
rabbit_mgmt_util:unprocessable_entity(Reason, ReqData1, Context)
end;
{error, tls_already_started} ->
rabbit_mgmt_util:unprocessable_entity("TLS configuration error: "
"cannot use StartTLS on an SSL connection "
"(use_ssl and use_starttls cannot both be true)",
ReqData1, Context);
Error ->
Reason = unicode_format(Error),
rabbit_mgmt_util:unprocessable_entity(Reason, ReqData1, Context)
end,
eldap:close(LDAP),
Result;
{error, E} ->
Reason = unicode_format("LDAP connection failed: ~tp "
"(servers: ~tp, user_dn: ~ts, password: ~s)",
[E, Servers, UserDN, format_password_for_logging(Password)]),
rabbit_mgmt_util:bad_request(Reason, ReqData1, Context)
end
catch throw:{bad_request, ErrMsg} ->
rabbit_mgmt_util:bad_request(ErrMsg, ReqData1, Context)
end
end,
rabbit_mgmt_util:with_decode([], ReqData0, Context, F).
%%--------------------------------------------------------------------
maybe_starttls(_LDAP, false, _BodyMap) ->
ok;
maybe_starttls(LDAP, true, BodyMap) ->
{ok, TlsOptions} = tls_options(BodyMap),
eldap:start_tls(LDAP, TlsOptions, 5000).
maybe_add_ssl_options(Options0, false, _BodyMap) ->
{ok, Options0};
maybe_add_ssl_options(Options0, true, BodyMap) ->
case maps:is_key(ssl_options, BodyMap) of
false ->
{ok, Options0};
true ->
Options1 = [{ssl, true} | Options0],
{ok, TlsOptions} = tls_options(BodyMap),
Options2 = [{sslopts, TlsOptions} | Options1],
{ok, Options2}
end.
tls_options(BodyMap) when is_map_key(ssl_options, BodyMap) ->
SslOptionsMap = maps:get(ssl_options, BodyMap),
case is_map(SslOptionsMap) of
false ->
throw({bad_request, "ssl_options must be a map/object"});
true ->
ok
end,
CaCertfile = maps:get(<<"cacertfile">>, SslOptionsMap, undefined),
CaCertPemData = maps:get(<<"cacert_pem_data">>, SslOptionsMap, undefined),
TlsOpts0 = case {CaCertfile, CaCertPemData} of
{undefined, undefined} ->
[{cacerts, public_key:cacerts_get()}];
_ ->
[]
end,
%% NB: for some reason the "cacertfile" key isn't turned into an atom
TlsOpts1 = case CaCertfile of
undefined ->
TlsOpts0;
CaCertfile ->
[{cacertfile, CaCertfile} | TlsOpts0]
end,
TlsOpts2 = case CaCertPemData of
undefined ->
TlsOpts1;
CaCertPems when is_list(CaCertPems) ->
F0 = fun (P) ->
try
case public_key:pem_decode(P) of
[{'Certificate', CaCertDerEncoded, not_encrypted}] ->
{true, CaCertDerEncoded};
[] ->
throw({bad_request, "invalid PEM data in cacert_pem_data: "
"no valid certificates found"});
_Unexpected ->
throw({bad_request, "unexpected cacert_pem_data passed to "
"/ldap/validate/simple-bind ssl_options.cacerts"})
end
catch
error:Reason ->
throw({bad_request, unicode_format("invalid PEM data in cacert_pem_data: ~tp", [Reason])})
end
end,
CaCertsDerEncoded = lists:filtermap(F0, CaCertPems),
[{cacerts, CaCertsDerEncoded} | TlsOpts1];
_ ->
TlsOpts1
end,
TlsOpts3 = case maps:get(<<"verify">>, SslOptionsMap, undefined) of
undefined ->
TlsOpts2;
Verify ->
try
VerifyStr = unicode:characters_to_list(Verify),
[{verify, list_to_existing_atom(VerifyStr)} | TlsOpts2]
catch
error:badarg ->
throw({bad_request, "invalid verify option passed to "
"/ldap/validate/simple-bind ssl_options.verify"})
end
end,
TlsOpts4 = case maps:get(<<"server_name_indication">>, SslOptionsMap, disable) of
disable ->
TlsOpts3;
SniValue ->
try
SniStr = unicode:characters_to_list(SniValue),
[{server_name_indication, SniStr} | TlsOpts3]
catch
error:badarg ->
throw({bad_request, "invalid server_name_indication: expected string"});
error:_ ->
throw({bad_request, "invalid server_name_indication: expected string"})
end
end,
TlsOpts5 = case maps:get(<<"depth">>, SslOptionsMap, undefined) of
undefined ->
TlsOpts4;
DepthValue ->
Depth = safe_parse_int(DepthValue, "ssl_options.depth"),
[{depth, Depth} | TlsOpts4]
end,
TlsOpts6 = case maps:get(<<"versions">>, SslOptionsMap, undefined) of
undefined ->
TlsOpts5;
VersionStrs when is_list(VersionStrs) ->
F1 = fun (VStr) ->
try
{true, list_to_existing_atom(VStr)}
catch error:badarg ->
throw({bad_request, "invalid TLS version passed to "
"/ldap/validate/simple-bind ssl_options.versions"})
end
end,
Versions = lists:filtermap(F1, VersionStrs),
[{versions, Versions} | TlsOpts5]
end,
TlsOpts7 = case maps:get(<<"ssl_hostname_verification">>, SslOptionsMap, undefined) of
undefined ->
TlsOpts6;
"wildcard" ->
[{customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]} | TlsOpts6];
_ ->
throw({bad_request, "invalid value passed to "
"/ldap/validate/simple-bind ssl_options.ssl_hostname_verification"})
end,
{ok, TlsOpts7};
tls_options(_BodyMap) ->
{ok, []}.
unicode_format(Arg) ->
rabbit_data_coercion:to_utf8_binary(io_lib:format("~tp", [Arg])).
unicode_format(Format, Args) ->
rabbit_data_coercion:to_utf8_binary(io_lib:format(Format, Args)).
format_password_for_logging(<<>>) ->
"[empty]";
format_password_for_logging(Password) ->
io_lib:format("[~p characters]", [string:length(Password)]).
safe_parse_int(Value, FieldName) ->
try
rabbit_mgmt_util:parse_int(Value)
catch
throw:{error, {not_integer, BadValue}} ->
Msg = unicode_format("invalid value for ~s: expected integer, got ~tp",
[FieldName, BadValue]),
throw({bad_request, Msg})
end.
safe_parse_bool(Value, FieldName) ->
try
rabbit_mgmt_util:parse_bool(Value)
catch
throw:{error, {not_boolean, BadValue}} ->
Msg = unicode_format("invalid value for ~s: expected boolean, got ~tp",
[FieldName, BadValue]),
throw({bad_request, Msg})
end.

View File

@ -11,9 +11,6 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
-include_lib("rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl").
-import(rabbit_mgmt_test_util, [http_put/4]).
-define(ALICE_NAME, "Alice").
-define(BOB_NAME, "Bob").
@ -100,7 +97,6 @@ all() ->
groups() ->
Tests = [
validate_ldap_configuration_via_api,
purge_connection,
ldap_only,
ldap_and_internal,
@ -162,23 +158,10 @@ end_per_group(_, Config) ->
init_slapd(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
CertsDir = ?config(rmq_certsdir, Config),
CaCertfile = filename:join([CertsDir, "testca", "cacert.pem"]),
ServerCertfile = filename:join([CertsDir, "server", "cert.pem"]),
ServerKeyfile = filename:join([CertsDir, "server", "key.pem"]),
TcpPort = 25389,
TlsPort = 25689,
SlapdDir = filename:join([PrivDir, "openldap"]),
InitSlapd = filename:join([DataDir, "init-slapd.sh"]),
Cmd = [
InitSlapd,
SlapdDir,
{"~b", [TcpPort]},
{"~b", [TlsPort]},
CaCertfile,
ServerCertfile,
ServerKeyfile
],
Cmd = [InitSlapd, SlapdDir, {"~b", [TcpPort]}],
case rabbit_ct_helpers:exec(Cmd) of
{ok, Stdout} ->
{match, [SlapdPid]} = re:run(
@ -191,8 +174,7 @@ init_slapd(Config) ->
[SlapdPid, TcpPort]),
rabbit_ct_helpers:set_config(Config,
[{slapd_pid, SlapdPid},
{ldap_port, TcpPort},
{ldap_tls_port, TlsPort}]);
{ldap_port, TcpPort}]);
_ ->
_ = rabbit_ct_helpers:exec(["pkill", "-INT", "slapd"]),
{skip, "Failed to initialize slapd(8)"}
@ -224,10 +206,6 @@ end_internal(Config) ->
ok = control_action(Config, delete_user, [?BOB_NAME]),
ok = control_action(Config, delete_user, [?PETER_NAME]).
init_per_testcase(validate_ldap_configuration_via_api = Testcase, Config) ->
_ = application:start(inets),
rabbit_ct_helpers:testcase_started(Config, Testcase);
init_per_testcase(Testcase, Config)
when Testcase == ldap_and_internal;
Testcase == internal_followed_ldap_and_internal ->
@ -251,9 +229,6 @@ init_per_testcase(Testcase, Config)
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
end_per_testcase(validate_ldap_configuration_via_api = Testcase, Config) ->
_ = application:stop(inets),
rabbit_ct_helpers:testcase_finished(Config, Testcase);
end_per_testcase(Testcase, Config)
when Testcase == ldap_and_internal;
Testcase == internal_followed_ldap_and_internal ->
@ -295,434 +270,6 @@ end_per_testcase(Testcase, Config) ->
%% Testsuite cases
%% -------------------------------------------------------------------
validate_ldap_configuration_via_api(Config) ->
CertsDir = ?config(rmq_certsdir, Config),
CaCertfile = filename:join([CertsDir, "testca", "cacert.pem"]),
%% {user_dn_pattern, "cn=${username},ou=People,dc=rabbitmq,dc=com"},
UserDNFmt = "cn=~ts,ou=People,dc=rabbitmq,dc=com",
AliceUserDN = rabbit_data_coercion:to_utf8_binary(io_lib:format(UserDNFmt, [?ALICE_NAME])),
InvalidUserDN = rabbit_data_coercion:to_utf8_binary(io_lib:format(UserDNFmt, ["NOBODY"])),
Password = rabbit_data_coercion:to_utf8_binary("password"),
LdapPort = ?config(ldap_port, Config),
LdapTlsPort = ?config(ldap_tls_port, Config),
%% NB: bad resource name
http_put(Config, "/ldap/validate/bad-bind-name",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
}, ?METHOD_NOT_ALLOWED),
%% Invalid JSON should return 400 Bad Request
rabbit_mgmt_test_util:http_put_raw(Config, "/ldap/validate/simple-bind",
"{invalid json", ?BAD_REQUEST),
%% HTTP Method coverage tests
%% GET method - should return 405 Method Not Allowed
?assertMatch({ok, {{_, ?METHOD_NOT_ALLOWED, _}, _Headers, _ResBody}},
rabbit_mgmt_test_util:req(Config, 0, get, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")])),
%% HEAD method - should return 405 Method Not Allowed (same as GET)
?assertMatch({ok, {{_, ?METHOD_NOT_ALLOWED, _}, _Headers, _ResBody}},
rabbit_mgmt_test_util:req(Config, 0, head, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")])),
%% POST method - should return 405 Method Not Allowed
?assertMatch({ok, {{_, ?METHOD_NOT_ALLOWED, _}, _Headers, _ResBody}},
rabbit_mgmt_test_util:req(Config, 0, post, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")], "{}")),
%% DELETE method - should return 405 Method Not Allowed
?assertMatch({ok, {{_, ?METHOD_NOT_ALLOWED, _}, _Headers, _ResBody}},
rabbit_mgmt_test_util:req(Config, 0, delete, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")])),
%% OPTIONS method - should return 200 with Allow header showing only PUT, OPTIONS
{ok, {{_, OptionsCode, _}, OptionsHeaders, _OptionsResBody}} =
rabbit_mgmt_test_util:req(Config, 0, options, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")]),
?assertEqual(?OK, OptionsCode),
AllowHeader = proplists:get_value("allow", OptionsHeaders),
?assert(string:str(string:to_upper(AllowHeader), "PUT") > 0),
?assert(string:str(string:to_upper(AllowHeader), "OPTIONS") > 0),
%% Should NOT contain GET or HEAD
?assertEqual(0, string:str(string:to_upper(AllowHeader), "GET")),
?assertEqual(0, string:str(string:to_upper(AllowHeader), "HEAD")),
%% Missing required fields tests
%% Empty servers array - connection failure (400)
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => [],
'port' => LdapPort
}, ?BAD_REQUEST),
%% Missing servers field entirely - defaults to [], same as above (400)
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'port' => LdapPort
}, ?BAD_REQUEST),
%% Missing user_dn field entirely - empty DN fails credential validation (422)
http_put(Config, "/ldap/validate/simple-bind",
#{
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
}, ?UNPROCESSABLE_ENTITY),
%% Missing password field entirely - empty password fails credential validation (422)
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'servers' => ["localhost"],
'port' => LdapPort
}, ?UNPROCESSABLE_ENTITY),
%% Invalid field values tests
%% Invalid port - string instead of number
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => "not_a_number"
}, ?BAD_REQUEST),
%% Invalid port - negative number
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => -1
}, ?BAD_REQUEST),
%% Invalid boolean - string instead of boolean
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort,
'use_ssl' => "maybe"
}, ?BAD_REQUEST),
%% Invalid servers - non-list value
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => "not_a_list",
'port' => LdapPort
}, ?BAD_REQUEST),
%% Network/Infrastructure scenarios
%% Non-existent server
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["nonexistent.example.com"],
'port' => LdapPort
}, ?BAD_REQUEST),
%% Invalid hostname format
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["not..a..valid..hostname"],
'port' => LdapPort
}, ?BAD_REQUEST),
%% Edge case credentials tests
%% Empty password - should be 422 (credential validation failure)
{ok, {{_, 422, _}, _Headers1, EmptyPasswordBody}} =
rabbit_mgmt_test_util:req(Config, 0, put, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")],
rabbit_mgmt_test_util:format_for_upload(#{
'user_dn' => AliceUserDN,
'password' => "",
'servers' => ["localhost"],
'port' => LdapPort
})),
EmptyPasswordJson = rabbit_json:decode(EmptyPasswordBody),
?assertEqual(<<"unprocessable_entity">>, maps:get(<<"error">>, EmptyPasswordJson)),
?assertEqual(<<"anonymous_auth">>, maps:get(<<"reason">>, EmptyPasswordJson)),
%% Empty user DN - should be 422 (credential validation failure)
{ok, {{_, 422, _}, _Headers2, EmptyUserDnBody}} =
rabbit_mgmt_test_util:req(Config, 0, put, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")],
rabbit_mgmt_test_util:format_for_upload(#{
'user_dn' => "",
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
})),
EmptyUserDnJson = rabbit_json:decode(EmptyUserDnBody),
?assertEqual(<<"unprocessable_entity">>, maps:get(<<"error">>, EmptyUserDnJson)),
?assertEqual(<<"anonymous_auth">>, maps:get(<<"reason">>, EmptyUserDnJson)),
%% Very long user DN (test size limits)
{ok, {{_, 422, _}, _Headers3, LongUserDnBody}} =
rabbit_mgmt_test_util:req(Config, 0, put, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")],
rabbit_mgmt_test_util:format_for_upload(#{
'user_dn' => binary:copy(<<"x">>, 10000),
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
})),
LongUserDnJson = rabbit_json:decode(LongUserDnBody),
?assertEqual(<<"unprocessable_entity">>, maps:get(<<"error">>, LongUserDnJson)),
?assertEqual(<<"invalid LDAP credentials: DN syntax invalid / too long">>,
maps:get(<<"reason">>, LongUserDnJson)),
%% Very long password (test size limits)
{ok, {{_, 422, _}, _Headers4, LongPasswordBody}} =
rabbit_mgmt_test_util:req(Config, 0, put, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")],
rabbit_mgmt_test_util:format_for_upload(#{
'user_dn' => AliceUserDN,
'password' => binary:copy(<<"x">>, 10000),
'servers' => ["localhost"],
'port' => LdapPort
})),
LongPasswordJson = rabbit_json:decode(LongPasswordBody),
?assertEqual(<<"unprocessable_entity">>, maps:get(<<"error">>, LongPasswordJson)),
?assertEqual(<<"invalid LDAP credentials: authentication failure">>,
maps:get(<<"reason">>, LongPasswordJson)),
%% SSL/TLS Edge Cases
%% Both use_ssl and use_starttls set to true - TLS configuration error
{ok, {{_, 422, _}, _Headers5, BothTlsBody}} =
rabbit_mgmt_test_util:req(Config, 0, put, "/ldap/validate/simple-bind",
[rabbit_mgmt_test_util:auth_header("guest", "guest")],
rabbit_mgmt_test_util:format_for_upload(#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'use_starttls' => true,
'ssl_options' => #{
'cacertfile' => CaCertfile
}
})),
BothTlsJson = rabbit_json:decode(BothTlsBody),
?assertEqual(<<"unprocessable_entity">>, maps:get(<<"error">>, BothTlsJson)),
?assertEqual(<<"TLS configuration error: cannot use StartTLS on an SSL connection (use_ssl and use_starttls cannot both be true)">>,
maps:get(<<"reason">>, BothTlsJson)),
%% Invalid certificate file path
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'cacertfile' => "/nonexistent/path/cert.pem"
}
}, ?BAD_REQUEST),
%% Invalid PEM data - should now return 400 Bad Request
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'cacert_pem_data' => ["not-valid-pem-data"]
}
}, ?BAD_REQUEST),
%% Invalid SSL options structure - not a map
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => "not_a_map"
}, ?BAD_REQUEST),
%% Invalid TLS versions
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'versions' => ["invalid_version", "tlsv1.2"],
'cacertfile' => CaCertfile
}
}, ?BAD_REQUEST),
%% Invalid verify option
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'verify' => "invalid_verify_option",
'cacertfile' => CaCertfile
}
}, ?BAD_REQUEST),
%% Invalid depth value - string instead of integer
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'depth' => "not_a_number",
'cacertfile' => CaCertfile
}
}, ?BAD_REQUEST),
%% Invalid server_name_indication - integer instead of string
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'server_name_indication' => 123,
'cacertfile' => CaCertfile
}
}, ?BAD_REQUEST),
%% Invalid server_name_indication - boolean instead of string
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'server_name_indication' => true,
'cacertfile' => CaCertfile
}
}, ?BAD_REQUEST),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
}, ?NO_CONTENT),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => InvalidUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort
}, ?UNPROCESSABLE_ENTITY),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'cacertfile' => CaCertfile
}
}, ?NO_CONTENT),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'server_name_indication' => "localhost",
'cacertfile' => CaCertfile
}
}, ?NO_CONTENT),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapPort,
'use_ssl' => false,
'use_starttls' => true,
'ssl_options' => #{
'server_name_indication' => "localhost",
'cacertfile' => CaCertfile
}
}, ?NO_CONTENT),
{ok, CaCertfileContent} = file:read_file(CaCertfile),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'versions' => ["tlsv1.2", "tlsv1.3"],
'depth' => 8,
'verify' => "verify_peer",
'cacert_pem_data' => [CaCertfileContent]
}
}, ?NO_CONTENT),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'versions' => ["tlsfoobar", "tlsv1.3"],
'depth' => 8,
'verify' => "verify_peer",
'cacert_pem_data' => [CaCertfileContent, CaCertfileContent]
}
}, ?BAD_REQUEST),
http_put(Config, "/ldap/validate/simple-bind",
#{
'user_dn' => AliceUserDN,
'password' => Password,
'servers' => ["localhost"],
'port' => LdapTlsPort,
'use_ssl' => true,
'ssl_options' => #{
'verify' => "verify_peer",
'cacertfile' => CaCertfile,
'ssl_hostname_verification' => "wildcard"
}
}, ?NO_CONTENT).
purge_connection(Config) ->
{ok, _} = rabbit_ct_broker_helpers:rpc(Config, 0,
rabbit_auth_backend_ldap,

View File

@ -1,18 +1,13 @@
#!/bin/sh
# vim:sw=4:et:
set -eux
set -ex
readonly slapd_data_dir="$1"
readonly tcp_port="$2"
readonly tls_port="$3"
readonly cacertfile="$4"
readonly server_certfile="$5"
readonly server_keyfile="$6"
readonly pidfile="$slapd_data_dir/slapd.pid"
readonly tcp_uri="ldap://localhost:$tcp_port"
readonly tls_uri="ldaps://localhost:$tls_port"
readonly uri="ldap://localhost:$tcp_port"
readonly binddn="cn=config"
readonly passwd=secret
@ -73,10 +68,6 @@ loglevel 7
database config
rootdn "$binddn"
rootpw $passwd
TLSCACertificateFile $cacertfile
TLSCertificateFile $server_certfile
TLSCertificateKeyFile $server_keyfile
EOF
cat "$conf_file"
@ -88,7 +79,7 @@ mkdir -p "$conf_dir"
"$slapd" \
-f "$conf_file" \
-F "$conf_dir" \
-h "$tcp_uri $tls_uri"
-h "$uri"
readonly auth="-x -D $binddn -w $passwd"
@ -96,7 +87,7 @@ readonly auth="-x -D $binddn -w $passwd"
# shellcheck disable=SC2034
for seconds in 1 2 3 4 5 6 7 8 9 10; do
# shellcheck disable=SC2086
ldapsearch $auth -H "$tcp_uri" -LLL -b cn=config dn && break;
ldapsearch $auth -H "$uri" -LLL -b cn=config dn && break;
sleep 1
done
@ -115,22 +106,22 @@ mkdir -p "$example_data_dir"
# shellcheck disable=SC2086
sed -E -e "s,^olcDbDirectory:.*,olcDbDirectory: $example_data_dir," \
< "$example_ldif_dir/global.ldif" | \
ldapadd $auth -H "$tcp_uri"
ldapadd $auth -H "$uri"
# We remove the module path from the example LDIF as it was already
# configured.
# shellcheck disable=SC2086
sed -E -e "s,^olcModulePath:.*,olcModulePath: $modulepath," \
< "$example_ldif_dir/memberof_init.ldif" | \
ldapadd $auth -H "$tcp_uri"
ldapadd $auth -H "$uri"
# shellcheck disable=SC2086
ldapmodify $auth -H "$tcp_uri" -f "$example_ldif_dir/refint_1.ldif"
ldapmodify $auth -H "$uri" -f "$example_ldif_dir/refint_1.ldif"
# shellcheck disable=SC2086
ldapadd $auth -H "$tcp_uri" -f "$example_ldif_dir/refint_2.ldif"
ldapadd $auth -H "$uri" -f "$example_ldif_dir/refint_2.ldif"
# shellcheck disable=SC2086
ldapsearch $auth -H "$tcp_uri" -LLL -b cn=config dn
ldapsearch $auth -H "$uri" -LLL -b cn=config dn
echo SLAPD_PID="$(cat "$pidfile")"

View File

@ -8,7 +8,6 @@
-define(BAD_REQUEST, 400).
-define(NOT_AUTHORISED, 401).
-define(METHOD_NOT_ALLOWED, 405).
-define(UNPROCESSABLE_ENTITY, 422).
%%-define(NOT_FOUND, 404). Defined for AMQP by amqp_client.hrl (as 404)
%% httpc seems to get racy when using HTTP 1.1
-define(HTTPC_OPTS, [{version, "HTTP/1.0"}, {autoredirect, false}]).

View File

@ -18,9 +18,8 @@
is_authorized_vhost_visible_for_monitoring/2,
is_authorized_global_parameters/2]).
-export([user/1]).
-export([bad_request/3, service_unavailable/3, not_authorised/3, bad_request_exception/4,
-export([bad_request/3, service_unavailable/3, bad_request_exception/4,
internal_server_error/3, internal_server_error/4, precondition_failed/3,
unprocessable_entity/3,
id/2, parse_bool/1, parse_int/1, redirect_to_home/3]).
-export([with_decode/4, not_found/3]).
-export([with_channel/4, with_channel/5]).
@ -676,12 +675,10 @@ a2b(B) -> B.
bad_request(Reason, ReqData, Context) ->
halt_response(400, bad_request, Reason, ReqData, Context).
unprocessable_entity(Reason, ReqData, Context) ->
halt_response(422, unprocessable_entity, Reason, ReqData, Context).
service_unavailable(Reason, ReqData, Context) ->
halt_response(503, service_unavailable, Reason, ReqData, Context).
not_authorised(Reason, ReqData, Context) ->
rabbit_web_dispatch_access_control:not_authorised(Reason, ReqData, Context).