diff --git a/deps/rabbitmq_auth_backend_ldap/Makefile b/deps/rabbitmq_auth_backend_ldap/Makefile index 987add9a8704..2224ea369a3b 100644 --- a/deps/rabbitmq_auth_backend_ldap/Makefile +++ b/deps/rabbitmq_auth_backend_ldap/Makefile @@ -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 diff --git a/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap_mgmt.erl b/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap_mgmt.erl deleted file mode 100644 index 13e11350fa52..000000000000 --- a/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap_mgmt.erl +++ /dev/null @@ -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. diff --git a/deps/rabbitmq_auth_backend_ldap/test/system_SUITE.erl b/deps/rabbitmq_auth_backend_ldap/test/system_SUITE.erl index 9ac4dd104de6..0a571ede5e8c 100644 --- a/deps/rabbitmq_auth_backend_ldap/test/system_SUITE.erl +++ b/deps/rabbitmq_auth_backend_ldap/test/system_SUITE.erl @@ -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, diff --git a/deps/rabbitmq_auth_backend_ldap/test/system_SUITE_data/init-slapd.sh b/deps/rabbitmq_auth_backend_ldap/test/system_SUITE_data/init-slapd.sh index 43b9aa503e61..2a9f9d3d4882 100755 --- a/deps/rabbitmq_auth_backend_ldap/test/system_SUITE_data/init-slapd.sh +++ b/deps/rabbitmq_auth_backend_ldap/test/system_SUITE_data/init-slapd.sh @@ -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")" diff --git a/deps/rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl b/deps/rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl index 88565b0781c7..857cc89467c7 100644 --- a/deps/rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl +++ b/deps/rabbitmq_ct_helpers/include/rabbit_mgmt_test.hrl @@ -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}]). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl index 7448b0cc7424..54fef24144d5 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl @@ -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).