From 7c0240f0ac79e7221edb133260738533b26bbe87 Mon Sep 17 00:00:00 2001 From: David Ansari Date: Tue, 12 Mar 2024 10:45:16 +0100 Subject: [PATCH] Add exchange deletion checks --- deps/rabbit/BUILD.bazel | 2 +- deps/rabbit/src/rabbit_access_control.erl | 4 +- deps/rabbit/src/rabbit_amqp_management.erl | 28 +++--- deps/rabbit/src/rabbit_amqp_util.erl | 2 +- deps/rabbit/test/amqp_auth_SUITE.erl | 46 ++++++++- deps/rabbitmq_amqp_client/app.bzl | 7 +- .../include/rabbitmq_amqp_client.hrl | 3 +- .../src/rabbitmq_amqp_client.erl | 96 +++++++++---------- .../test/management_SUITE.erl | 84 ++++++++++------ 9 files changed, 171 insertions(+), 101 deletions(-) diff --git a/deps/rabbit/BUILD.bazel b/deps/rabbit/BUILD.bazel index be46052ea417..96720b81bb4d 100644 --- a/deps/rabbit/BUILD.bazel +++ b/deps/rabbit/BUILD.bazel @@ -1279,7 +1279,7 @@ rabbitmq_integration_suite( ":test_event_recorder_beam", ], runtime_deps = [ - "//deps/amqp10_client:erlang_app", + "//deps/rabbitmq_amqp_client:erlang_app", ], ) diff --git a/deps/rabbit/src/rabbit_access_control.erl b/deps/rabbit/src/rabbit_access_control.erl index 6c3da3cd8d9d..bc6d24372a55 100644 --- a/deps/rabbit/src/rabbit_access_control.erl +++ b/deps/rabbit/src/rabbit_access_control.erl @@ -189,7 +189,7 @@ check_resource_access(User = #user{username = Username, check_access( fun() -> Module:check_resource_access( auth_user(User, Impl), Resource, Permission, Context) end, - Module, "~s access to ~s refused for user '~s'", + Module, "~s access to ~ts refused for user '~ts'", [Permission, rabbit_misc:rs(Resource), Username]); (_, Else) -> Else end, ok, Modules). @@ -202,7 +202,7 @@ check_topic_access(User = #user{username = Username, check_access( fun() -> Module:check_topic_access( auth_user(User, Impl), Resource, Permission, Context) end, - Module, "~s access to topic '~s' in exchange ~s refused for user '~s'", + Module, "~s access to topic '~ts' in exchange ~s refused for user '~ts'", [Permission, maps:get(routing_key, Context), rabbit_misc:rs(Resource), Username]); (_, Else) -> Else end, ok, Modules). diff --git a/deps/rabbit/src/rabbit_amqp_management.erl b/deps/rabbit/src/rabbit_amqp_management.erl index edd64030e471..ad3eb4b775ae 100644 --- a/deps/rabbit/src/rabbit_amqp_management.erl +++ b/deps/rabbit/src/rabbit_amqp_management.erl @@ -148,22 +148,19 @@ handle_http_req(<<"DELETE">>, {<<"200">>, [], RespPayload}; handle_http_req(<<"DELETE">>, - [<<"exchanges">>, XNameBinQ], + [<<"exchanges">>, XNameBinQuoted], _Query, null, Vhost, - #user{username = Username}, + User = #user{username = Username}, _ConnPid) -> - XNameBin = uri_string:unquote(XNameBinQ), + XNameBin = uri_string:unquote(XNameBinQuoted), XName = rabbit_misc:r(Vhost, exchange, XNameBin), - ok = case rabbit_exchange:delete(XName, false, Username) of - ok -> - ok; - {error, not_found} -> - ok - %% %% TODO return deletion failure - %% {error, in_use} -> - end, + ok = prohibit_cr_lf(XNameBin), + ok = prohibit_default_exchange(XNameBin), + ok = prohibit_reserved_amq(XName), + ok = check_resource_access(XName, configure, User), + _ = rabbit_exchange:delete(XName, false, Username), {<<"204">>, [], null}; handle_http_req(<<"POST">>, @@ -457,13 +454,12 @@ prohibit_reserved_amq(#resource{}) -> rabbit_types:user()) -> ok. check_resource_access(Resource, Perm, User) -> try rabbit_access_control:check_resource_access(User, Resource, Perm, #{}) - catch exit:#amqp_error{name = not_allowed} -> + catch exit:#amqp_error{name = access_refused, + explanation = Explanation} -> %% For authorization failures, let's be more strict: Close the entire - %% AMQP session instead of only returning a HTTP Status Code 403. + %% AMQP session instead of only returning an HTTP Status Code 403. rabbit_amqp_util:protocol_error( - ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, - "~s access refused for user '~ts' to ~ts", - [Perm, User, rabbit_misc:rs(Resource)]) + ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, Explanation, []) end. -spec throw(binary(), io:format(), [term()]) -> no_return(). diff --git a/deps/rabbit/src/rabbit_amqp_util.erl b/deps/rabbit/src/rabbit_amqp_util.erl index 0398c5c38b56..3257cef93704 100644 --- a/deps/rabbit/src/rabbit_amqp_util.erl +++ b/deps/rabbit/src/rabbit_amqp_util.erl @@ -13,7 +13,7 @@ -spec protocol_error(term(), io:format(), [term()]) -> no_return(). protocol_error(Condition, Msg, Args) -> - Description = list_to_binary(lists:flatten(io_lib:format(Msg, Args))), + Description = unicode:characters_to_binary(lists:flatten(io_lib:format(Msg, Args))), Reason = #'v1_0.error'{condition = Condition, description = {utf8, Description}}, exit(Reason). diff --git a/deps/rabbit/test/amqp_auth_SUITE.erl b/deps/rabbit/test/amqp_auth_SUITE.erl index eafa0f1cabd4..06296be4968e 100644 --- a/deps/rabbit/test/amqp_auth_SUITE.erl +++ b/deps/rabbit/test/amqp_auth_SUITE.erl @@ -47,7 +47,11 @@ groups() -> vhost_absent, vhost_connection_limit, user_connection_limit, - vhost_queue_limit + vhost_queue_limit, + + %% AMQP Management operations against HTTP API v2 + declare_exchange, + delete_exchange ] } ]. @@ -537,6 +541,46 @@ vhost_queue_limit(Config) -> ok = close_connection_sync(C2), ok = rabbit_ct_broker_helpers:clear_vhost_limit(Config, 0, Vhost). +declare_exchange(Config) -> + {Conn, _Session, LinkPair} = init_pair(Config), + XName = <<"šŸ“®"/utf8>>, + ExpectedErr = error_unauthorized( + <<"configure access to exchange '", XName/binary, + "' in vhost 'test vhost' refused for user 'test user'">>), + ?assertEqual({error, {session_ended, ExpectedErr}}, + rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{})), + ok = close_connection_sync(Conn). + +delete_exchange(Config) -> + {Conn1, _, LinkPair1} = init_pair(Config), + XName = <<"šŸ“®"/utf8>>, + ok = set_permissions(Config, XName, <<>>, <<>>), + ok = rabbitmq_amqp_client:declare_exchange(LinkPair1, XName, #{}), + ok = clear_permissions(Config), + ExpectedErr = error_unauthorized( + <<"configure access to exchange '", XName/binary, + "' in vhost 'test vhost' refused for user 'test user'">>), + ?assertEqual({error, {session_ended, ExpectedErr}}, + rabbitmq_amqp_client:delete_exchange(LinkPair1, XName)), + ok = close_connection_sync(Conn1), + + ok = set_permissions(Config, XName, <<>>, <<>>), + Init = {_, _, LinkPair2} = init_pair(Config), + ok = rabbitmq_amqp_client:delete_exchange(LinkPair2, XName), + ok = cleanup_pair(Init). + +init_pair(Config) -> + OpnConf = connection_config(Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"mgmt link pair">>), + {Connection, Session, LinkPair}. + +cleanup_pair({Connection, Session, LinkPair}) -> + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = amqp10_client:end_session(Session), + ok = amqp10_client:close_connection(Connection). + connection_config(Config) -> Vhost = ?config(test_vhost, Config), connection_config(Config, Vhost). diff --git a/deps/rabbitmq_amqp_client/app.bzl b/deps/rabbitmq_amqp_client/app.bzl index 2b7007ce28d3..6f3e3c4c0446 100644 --- a/deps/rabbitmq_amqp_client/app.bzl +++ b/deps/rabbitmq_amqp_client/app.bzl @@ -22,9 +22,10 @@ def all_srcs(name = "all_srcs"): srcs = ["src/rabbitmq_amqp_client.erl"], ) filegroup(name = "private_hdrs") - filegroup(name = "public_hdrs", srcs = [ - "include/rabbitmq_amqp_client.hrl", - ]) + filegroup( + name = "public_hdrs", + srcs = ["include/rabbitmq_amqp_client.hrl"], + ) filegroup(name = "priv") filegroup( name = "license_files", diff --git a/deps/rabbitmq_amqp_client/include/rabbitmq_amqp_client.hrl b/deps/rabbitmq_amqp_client/include/rabbitmq_amqp_client.hrl index bb5dcf24da51..58ba4dab1d8a 100644 --- a/deps/rabbitmq_amqp_client/include/rabbitmq_amqp_client.hrl +++ b/deps/rabbitmq_amqp_client/include/rabbitmq_amqp_client.hrl @@ -1,3 +1,4 @@ --record(link_pair, {outgoing_link :: amqp10_client:link_ref(), +-record(link_pair, {session :: pid(), + outgoing_link :: amqp10_client:link_ref(), incoming_link :: amqp10_client:link_ref()}). -type link_pair() :: #link_pair{}. diff --git a/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl b/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl index fecd6d0b25cc..8ed2060e7b12 100644 --- a/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl +++ b/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl @@ -13,8 +13,8 @@ -export[attach_management_link_pair_sync/2, detach_management_link_pair_sync/1, - declare_queue/2, - declare_exchange/2, + declare_queue/3, + declare_exchange/3, bind_queue/5, bind_exchange/5, unbind_queue/5, @@ -61,7 +61,8 @@ attach_management_link_pair_sync(Session, Name) -> {ok, IncomingRef} ?= attach(Session, IncomingAttachArgs), ok ?= await_attached(OutgoingRef), ok ?= await_attached(IncomingRef), - {ok, #link_pair{outgoing_link = OutgoingRef, + {ok, #link_pair{session = Session, + outgoing_link = OutgoingRef, incoming_link = IncomingRef}} end. @@ -117,28 +118,26 @@ await_detached(Ref) -> {error, timeout} end. --spec declare_queue(link_pair(), queue_properties()) -> +-spec declare_queue(link_pair(), binary(), queue_properties()) -> {ok, map()} | {error, term()}. -declare_queue(LinkPair, QueueProperties) -> - {QName, Body0} = maps:fold( - fun(name, V, {undefined, L}) when is_binary(V) -> - {V, L}; - (durable, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"durable">>}, {boolean, V}} | L]}; - (exclusive, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"exclusive">>}, {boolean, V}} | L]}; - (auto_delete, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"auto_delete">>}, {boolean, V}} | L]}; - (arguments, V, {N, L0}) -> - KVList = maps:fold( - fun(K = <<"x-", _/binary>>, TaggedVal = {T, _}, L) - when is_atom(T) -> - [{{utf8, K}, TaggedVal} | L] - end, [], V), - {N, [{{utf8, <<"arguments">>}, {map, KVList}} | L0]} - end, {undefined, []}, QueueProperties), +declare_queue(LinkPair, QueueName, QueueProperties) -> + Body0 = maps:fold( + fun(durable, V, L) when is_boolean(V) -> + [{{utf8, <<"durable">>}, {boolean, V}} | L]; + (exclusive, V, L) when is_boolean(V) -> + [{{utf8, <<"exclusive">>}, {boolean, V}} | L]; + (auto_delete, V, L) when is_boolean(V) -> + [{{utf8, <<"auto_delete">>}, {boolean, V}} | L]; + (arguments, V, L) -> + KVList = maps:fold( + fun(K = <<"x-", _/binary>>, TaggedVal = {T, _}, L0) + when is_atom(T) -> + [{{utf8, K}, TaggedVal} | L0] + end, [], V), + [{{utf8, <<"arguments">>}, {map, KVList}} | L] + end, [], QueueProperties), Body = {map, Body0}, - QNameQuoted = uri_string:quote(QName), + QNameQuoted = uri_string:quote(QueueName), Props = #{subject => <<"PUT">>, to => <<"/queues/", QNameQuoted/binary>>}, @@ -316,31 +315,29 @@ purge_or_delete_queue(LinkPair, QueueName, PathSuffix) -> Err end. --spec declare_exchange(link_pair(), exchange_properties()) -> +-spec declare_exchange(link_pair(), binary(), exchange_properties()) -> ok | {error, term()}. -declare_exchange(LinkPair, ExchangeProperties) -> - {XName, Body0} = maps:fold( - fun(name, V, {undefined, L}) when is_binary(V) -> - {V, L}; - (type, V, {N, L}) when is_binary(V) -> - {N, [{{utf8, <<"type">>}, {utf8, V}} | L]}; - (durable, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"durable">>}, {boolean, V}} | L]}; - (auto_delete, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"auto_delete">>}, {boolean, V}} | L]}; - (internal, V, {N, L}) when is_boolean(V) -> - {N, [{{utf8, <<"internal">>}, {boolean, V}} | L]}; - (arguments, V, {N, L0}) -> - KVList = maps:fold( - fun(K = <<"x-", _/binary>>, TaggedVal = {T, _}, L) - when is_atom(T) -> - [{{utf8, K}, TaggedVal} | L] - end, [], V), - {N, [{{utf8, <<"arguments">>}, {map, KVList}} | L0]} - end, {undefined, []}, ExchangeProperties), +declare_exchange(LinkPair, ExchangeName, ExchangeProperties) -> + Body0 = maps:fold( + fun(type, V, L) when is_binary(V) -> + [{{utf8, <<"type">>}, {utf8, V}} | L]; + (durable, V, L) when is_boolean(V) -> + [{{utf8, <<"durable">>}, {boolean, V}} | L]; + (auto_delete, V, L) when is_boolean(V) -> + [{{utf8, <<"auto_delete">>}, {boolean, V}} | L]; + (internal, V, L) when is_boolean(V) -> + [{{utf8, <<"internal">>}, {boolean, V}} | L]; + (arguments, V, L) -> + KVList = maps:fold( + fun(K = <<"x-", _/binary>>, TaggedVal = {T, _}, L0) + when is_atom(T) -> + [{{utf8, K}, TaggedVal} | L0] + end, [], V), + [{{utf8, <<"arguments">>}, {map, KVList}} | L] + end, [], ExchangeProperties), Body = {map, Body0}, - XNameQuoted = uri_string:quote(XName), + XNameQuoted = uri_string:quote(ExchangeName), Props = #{subject => <<"PUT">>, to => <<"/exchanges/", XNameQuoted/binary>>}, @@ -377,7 +374,8 @@ delete_exchange(LinkPair, ExchangeName) -> -spec request(link_pair(), amqp10_msg:amqp10_properties(), amqp10_prim()) -> {ok, Response :: amqp10_msg:amqp10_msg()} | {error, term()}. -request(#link_pair{outgoing_link = OutgoingLink, +request(#link_pair{session = Session, + outgoing_link = OutgoingLink, incoming_link = IncomingLink}, Properties, Body) -> MessageId = message_id(), Properties1 = Properties#{message_id => {binary, MessageId}, @@ -389,9 +387,11 @@ request(#link_pair{outgoing_link = OutgoingLink, ok -> receive {amqp10_msg, IncomingLink, Response} -> #{correlation_id := MessageId} = amqp10_msg:properties(Response), - {ok, Response} + {ok, Response}; + {amqp10_event, {session, Session, {ended, Reason}}} -> + {error, {session_ended, Reason}} after ?TIMEOUT -> - {error, response_timeout} + {error, timeout} end; Err -> Err diff --git a/deps/rabbitmq_amqp_client/test/management_SUITE.erl b/deps/rabbitmq_amqp_client/test/management_SUITE.erl index 8c0e2dcfc8fb..b115e5241480 100644 --- a/deps/rabbitmq_amqp_client/test/management_SUITE.erl +++ b/deps/rabbitmq_amqp_client/test/management_SUITE.erl @@ -45,8 +45,12 @@ groups() -> bad_exchange_property, bad_exchange_type, declare_default_exchange, + declare_exchange_amq_prefix, declare_exchange_line_feed, - declare_exchange_inequivalent_fields + declare_exchange_inequivalent_fields, + delete_default_exchange, + delete_exchange_amq_prefix, + delete_exchange_carriage_return ]} ]. @@ -83,12 +87,11 @@ end_per_testcase(Testcase, Config) -> all_management_operations(Config) -> Init = {_, Session, LinkPair} = init(Config), QName = <<"my šŸ‡"/utf8>>, - QProps = #{name => QName, - durable => true, + QProps = #{durable => true, exclusive => false, auto_delete => false, arguments => #{<<"x-queue-type">> => {symbol, <<"quorum">>}}}, - {ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, QProps), + {ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, QProps), [Q] = rpc(Config, rabbit_amqqueue, list, []), ?assert(rpc(Config, amqqueue, is_durable, [Q])), @@ -124,13 +127,12 @@ all_management_operations(Config) -> ok = wait_for_settlement(DTag3, released), XName = <<"my fanout exchange šŸ„³"/utf8>>, - XProps = #{name => XName, - type => <<"fanout">>, + XProps = #{type => <<"fanout">>, durable => false, auto_delete => true, internal => false, arguments => #{<<"x-šŸ“„"/utf8>> => {utf8, <<"šŸ“®"/utf8>>}}}, - ?assertEqual(ok, rabbitmq_amqp_client:declare_exchange(LinkPair, XProps)), + ?assertEqual(ok, rabbitmq_amqp_client:declare_exchange(LinkPair, XName, XProps)), {ok, Exchange} = rpc(Config, rabbit_exchange, lookup, [rabbit_misc:r(<<"/">>, exchange, XName)]), ?assertMatch(#exchange{type = fanout, @@ -193,7 +195,7 @@ all_management_operations(Config) -> queue_defaults(Config) -> Init = {_, _, LinkPair} = init(Config), QName = atom_to_binary(?FUNCTION_NAME), - {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, #{name => QName}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), [Q] = rpc(Config, rabbit_amqqueue, list, []), ?assert(rpc(Config, amqqueue, is_durable, [Q])), ?assertNot(rpc(Config, amqqueue, is_exclusive, [Q])), @@ -206,10 +208,9 @@ queue_defaults(Config) -> queue_properties(Config) -> Init = {_, _, LinkPair} = init(Config), QName = atom_to_binary(?FUNCTION_NAME), - {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, #{name => QName, - durable => false, - exclusive => true, - auto_delete => true}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{durable => false, + exclusive => true, + auto_delete => true}), [Q] = rpc(Config, rabbit_amqqueue, list, []), ?assertNot(rpc(Config, amqqueue, is_durable, [Q])), ?assert(rpc(Config, amqqueue, is_exclusive, [Q])), @@ -221,7 +222,7 @@ queue_properties(Config) -> exchange_defaults(Config) -> Init = {_, _, LinkPair} = init(Config), XName = atom_to_binary(?FUNCTION_NAME), - ok = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => XName}), + ok = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{}), {ok, Exchange} = rpc(Config, rabbit_exchange, lookup, [rabbit_misc:r(<<"/">>, exchange, XName)]), ?assertMatch(#exchange{type = direct, durable = true, @@ -236,12 +237,11 @@ exchange_defaults(Config) -> queue_binding_args(Config) -> Init = {_, Session, LinkPair} = init(Config), QName = <<"my queue ~!@#$%^&*()_+šŸ™ˆ`-=[]\;',./"/utf8>>, - Q = #{name => QName, - durable => false, + Q = #{durable => false, exclusive => true, auto_delete => false, arguments => #{<<"x-queue-type">> => {symbol, <<"classic">>}}}, - {ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, Q), + {ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, Q), Exchange = <<"amq.headers">>, BindingKey = <<>>, @@ -347,8 +347,7 @@ bad_property(Kind, Config) -> bad_exchange_type(Config) -> Init = {_, _, LinkPair} = init(Config), UnknownXType = <<"šŸ¤·"/utf8>>, - {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => <<"e1">>, - type => UnknownXType}), + {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, <<"e1">>, #{type => UnknownXType}), ?assertMatch(#{subject := <<"400">>}, amqp10_msg:properties(Resp)), ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"unknown exchange type '", UnknownXType/binary, "'">>}}, amqp10_msg:body(Resp)), @@ -357,16 +356,27 @@ bad_exchange_type(Config) -> declare_default_exchange(Config) -> Init = {_, _, LinkPair} = init(Config), DefaultX = <<"">>, - {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => DefaultX}), + {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, DefaultX, #{}), ?assertMatch(#{subject := <<"403">>}, amqp10_msg:properties(Resp)), ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"operation not permitted on the default exchange">>}}, amqp10_msg:body(Resp)), ok = cleanup(Init). +declare_exchange_amq_prefix(Config) -> + Init = {_, _, LinkPair} = init(Config), + XName = <<"amq.šŸŽ‡"/utf8>>, + {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{}), + ?assertMatch(#{subject := <<"403">>}, amqp10_msg:properties(Resp)), + ?assertEqual(#'v1_0.amqp_value'{ + content = {utf8, <<"exchange '", XName/binary, "' in vhost '/' " + "starts with reserved prefix 'amq.'">>}}, + amqp10_msg:body(Resp)), + ok = cleanup(Init). + declare_exchange_line_feed(Config) -> Init = {_, _, LinkPair} = init(Config), XName = <<"šŸ¤ \nšŸ˜±"/utf8>>, - {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => XName}), + {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{}), ?assertMatch(#{subject := <<"400">>}, amqp10_msg:properties(Resp)), ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"Bad name '", XName/binary, "': \n and \r not allowed">>}}, amqp10_msg:body(Resp)), @@ -375,19 +385,28 @@ declare_exchange_line_feed(Config) -> declare_exchange_inequivalent_fields(Config) -> Init = {_, _, LinkPair} = init(Config), XName = <<"šŸ‘Œ"/utf8>>, - ok = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => XName, - type => <<"direct">>}), - {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => XName, - type => <<"fanout">>}), + ok = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{type => <<"direct">>}), + {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{type => <<"fanout">>}), ?assertMatch(#{subject := <<"409">>}, amqp10_msg:properties(Resp)), - ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"inequivalent arg 'type' for exchange '", XName/binary, "' in vhost '/': received 'fanout' but current is 'direct'">>}}, + ?assertEqual(#'v1_0.amqp_value'{ + content = {utf8, <<"inequivalent arg 'type' for exchange '", XName/binary, + "' in vhost '/': received 'fanout' but current is 'direct'">>}}, amqp10_msg:body(Resp)), ok = cleanup(Init). -declare_exchange_amq_prefix(Config) -> +delete_default_exchange(Config) -> Init = {_, _, LinkPair} = init(Config), - XName = <<"amq.šŸŽ‡"/utf8>>, - {error, Resp} = rabbitmq_amqp_client:declare_exchange(LinkPair, #{name => XName}), + DefaultX = <<"">>, + {error, Resp} = rabbitmq_amqp_client:delete_exchange(LinkPair, DefaultX), + ?assertMatch(#{subject := <<"403">>}, amqp10_msg:properties(Resp)), + ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"operation not permitted on the default exchange">>}}, + amqp10_msg:body(Resp)), + ok = cleanup(Init). + +delete_exchange_amq_prefix(Config) -> + Init = {_, _, LinkPair} = init(Config), + XName = <<"amq.fanout">>, + {error, Resp} = rabbitmq_amqp_client:delete_exchange(LinkPair, XName), ?assertMatch(#{subject := <<"403">>}, amqp10_msg:properties(Resp)), ?assertEqual(#'v1_0.amqp_value'{ content = {utf8, <<"exchange '", XName/binary, "' in vhost '/' " @@ -395,6 +414,15 @@ declare_exchange_amq_prefix(Config) -> amqp10_msg:body(Resp)), ok = cleanup(Init). +delete_exchange_carriage_return(Config) -> + Init = {_, _, LinkPair} = init(Config), + XName = <<"x\rx">>, + {error, Resp} = rabbitmq_amqp_client:delete_exchange(LinkPair, XName), + ?assertMatch(#{subject := <<"400">>}, amqp10_msg:properties(Resp)), + ?assertEqual(#'v1_0.amqp_value'{content = {utf8, <<"Bad name '", XName/binary, "': \n and \r not allowed">>}}, + amqp10_msg:body(Resp)), + ok = cleanup(Init). + init(Config) -> OpnConf = connection_config(Config), {ok, Connection} = amqp10_client:open_connection(OpnConf),